How can I access viewcontroller's NSTimer from TabBarController in Swift - swift

I'm new on Swift. I have a Tab Bar Controller in my ios app. I am starting NSTimer in my first tab which name is as HomePageViewController as below :
override func viewDidLoad() {
super.viewDidLoad(){
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateTimer", userInfo: nil, repeats: true)
}
func stopTimer(){
print("stop timer")
self.timer?.invalidate()
self.timer=nil
}
func reStartTimer(){
print("restart timer")
timer?.invalidate()
timer=nil
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateTimer", userInfo: nil, repeats: true)
}
My didSelectItem method in TabBarController.swift is :
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
if item == (self.tabBar.items![0]){
//necessary operation
}
else {
homePageVC.stopTimer()
}
but timer doesn't stop. How can I stop timer from TabBarViewController ?

You should invalidate your timer. See the documentation here.
if item == (self.tabBar.items![0]){
//necessary operation
} else {
homePageVC.invalidate()
}

ok I fixed problem :
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
let homePageVC = self.viewControllers![0] as! HomePageViewController
if item == (self.tabBar.items![0]){
if (lastSelectedTabItem==0){
}
else{
homePageVC.reStartTimer()
}
}
else {
homePageVC.stopTimer()
}
if(item.title=="Tab1"){
lastSelectedTabItem=0
}
else if(item.title=="Tab2"){
lastSelectedTabItem=1
}
else if(item.title=="Tab3"){
lastSelectedTabItem=2
}
else {
lastSelectedTabItem=3
}
}

Related

Timer failure in UNUserNotificationCenter.getNotificationSettings

I'm trying to start some timers on a view controller, based on whether or not the application has provided push permissions, but the timers are not triggering. Can anyone explain to me why that is?
class SomeViewController: UIViewController {
private var startTime: Date!
private var countDown: Timer?
private var timer: Timer?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTime = Date()
UNUserNotificationCenter.current().getNotificationSettings { settings in
if settings.authorizationStatus == .denied || settings.authorizationStatus == .notDetermined {
self.timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.manualFunction(_:)), userInfo: nil, repeats: true)
} else {
// Wait two seconds, then start the manual check.
self.countDown = Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { (timer) in
self.timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.manualFunction(_:)), userInfo: nil, repeats: true)
})
}
}
}
#objc private func manualFunction(_ timer: Timer) {
// Some function I want to execute whenever the second timer triggers.
}
}
The viewDidAppear function used to contain the following code that did work:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTime = Date()
self.countDown = Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { [weak self] (timer) in
guard let self = self else { return }
self.timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.manualFunction(_:)), userInfo: nil, repeats: true)
})
}
I've tried making the timers weak, I've tried adding [unowned self] and [weak self] in several spots, without a success. Can someone explain this behaviour?
When calling the getNotificationSettings method you're entering the background queue and you should come back to your main queue to handle task that are to be performed in the main queue.
UNUserNotificationCenter.current().getNotificationSettings { settings in
DispatchQueue.main.async {
// Add your tasks here
}
}

How to stop backgroundUpdateTask?

Here is code which I execute in background when user close the app, but it is weird behavior , after endBackgroundUpdateTask() method is executed , DispatchQueue still doesn't stops...
I steel continuos get notification.
What am I doing wrong?
you can try to take this snipped of code and try for yourself, it is really weird
var backgroundUpdateTask: UIBackgroundTaskIdentifier!
func beginBackgroundUpdateTask() {
print("beginBackgroundUpdateTask")
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
print("endBackgroundUpdateTask")
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
func doBackgroundTask() {
print("Strart")
DispatchQueue.global().async {
self.beginBackgroundUpdateTask()
// Do something with the result.
let timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(AppDelegate.displayAlert), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
// End the background task.
self.endBackgroundUpdateTask()
}
print("Finish")
}
func displayAlert() {
print("displayAlert")
let note = UILocalNotification()
note.alertBody = "As a test I'm hoping this will run in the background every X number of seconds..."
note.soundName = UILocalNotificationDefaultSoundName
UIApplication.shared.scheduleLocalNotification(note)
}
func applicationDidEnterBackground(_ application: UIApplication) {
log.debug("applicationDidEnterBackground")
self.doBackgroundTask()
}
edit
var backgroundUpdateTask: UIBackgroundTaskIdentifier!
var timerr: Timer?
func beginBackgroundUpdateTask() {
appDeligate.log.debug("beginBackgroundUpdateTask")
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
appDeligate.log.debug("endBackgroundUpdateTask")
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
timerr = nil
}
func doBackgroundTask() {
print("Strart")
DispatchQueue.global().async {
self.beginBackgroundUpdateTask()
// Do something with the result.
self.timerr = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(TestViewController.displayAlert), userInfo: nil, repeats: true)
RunLoop.current.add(self.timerr!, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
// End the background task.
self.endBackgroundUpdateTask()
}
print("Finish")
}
func displayAlert() {
print("displayAlert")
let note = UILocalNotification()
note.alertBody = "As a test I'm hoping this will run in the background every X number of seconds..."
note.soundName = UILocalNotificationDefaultSoundName
UIApplication.shared.scheduleLocalNotification(note)
}
Really issue was with Timer, I was needed put this line
timerr?.invalidate()
here
func endBackgroundUpdateTask() {
appDeligate.log.debug("endBackgroundUpdateTask")
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
timerr?.invalidate()
}
But anyway it is little bit weird , because I thought if I was stopping backgroundUpdate() all tasks inside had to invalidate and purge automatically, but no.
Thanks #matt

How is that possible to post notification after 1 second delay or in a dispatch asyn?

i just want to call a method after loading my view. i have set notification to one view and postnotification to other view you will get more idea from my code.. here is my code..
in a one.swift file
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "InsertNote:", name: "insert", object: nil)
}
func InsertNote(notification:NSNotification)
{
println("blablalbla")
}
in a second.swift file
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
}
func getData()
{
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
i have try also in a dispatch_asynch but still it is not work.
Try this
let delayInSeconds: Double = 1
override func viewDidLoad() {
super.viewDidLoad()
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue()){
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
}
The only reason I can come up with that this doesn't work, is that your first ViewController A is already deallocated when you send the notification with the second ViewController B. So, you are sending the notification with B correctly, but A isn't around to receive it.
You can test this by adding an observer in B as well.
override func viewDidLoad() {
super.viewDidLoad()
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("getData"), userInfo: nil, repeats: false)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "insertNote:", name: "insert", object: nil)
}
func getData() {
NSNotificationCenter.defaultCenter().postNotificationName("insert", object: nil)
}
func insertNote:(note: NSNotification) {
print("Banana")
}
// Should print 'Banana' after one second.

Timer.fire() not working after invalidating in Swift

After using
#IBAction func pauseButton(sender: AnyObject) {
if isPaused == false {
timer.invalidate()
isPaused = true
displayLabel.text = "\(count)"
println("App is paused equals \(isPaused)")
} else if isPaused == true {
var isPaused = false
timer.fire()
// timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
}
to pause the app, i'd like to hit pause again, in which case, the timer will continue to count from where it left off.
Additionally, when i press pause/play too many times, multiple instances of the timer occur which will cause the timer to increase a few times per second.
Please help!
//
// ViewController.swift
// Navigation Bars
//
// Created by Alex Ngounou on 8/27/15.
// Copyright (c) 2015 Alex Ngounou. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
var timer = NSTimer()
var count = 0
var isPaused = false
func updateTime() {
switch count {
case 0, 1:
count++
println( "\(count) second.")
displayLabel.text = "\(count)"
case 2:
count++
println("\(count) seconds.")
displayLabel.text = "\(count)"
default:
count++
println("\(count) seconds.")
displayLabel.text = "\(count)"
}
}
**strong text**#IBAction func playButton(sender: AnyObject) {
var isPaused = false
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
#IBAction func stopButton(sender: AnyObject) {
timer.invalidate()
count = 0
displayLabel.text = "0"
}
// if it's currently paused, pressing on the pause button again should restart the counter from where it originally left off.
#IBAction func pauseButton(sender: AnyObject) {
if isPaused == false {
timer.invalidate()
isPaused = true
displayLabel.text = "\(count)"
println("App is paused equals \(isPaused)")
} else if isPaused == true {
var isPaused = false
timer.fire()
// timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
}
#IBAction func resetButton(sender: AnyObject) {
timer.invalidate()
count = 0
displayLabel.text = ""
}
#IBOutlet weak var displayLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
From: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/
Once invalidated, timer objects cannot be reused.
So essentially an NSTimer will do nothing at all once invalidated. Your timer property must be assigned to a newly constructed NSTimer object after that point to get it to fire again. If your invalidation bookkeeping is accurate, there is no "buildup" problem of multiple timers.
Probably the easiest method to your actual problem, though, is logical filtering. That is, keep the NSTimer object around indefinitely and let it fire continually. When the stored property isPaused is true, you ignore timer events (by returning immediately from the processing function), otherwise you process them.
a "lazier" approach can be otherwise useful:
class ViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
startTimer()
}
final func killTimer(){
self.timer?.invalidate()
self.timer = nil
}
final private func startTimer() {
// make it re-entrant:
// if timer is running, kill it and start from scratch
self.killTimer()
let fire = Date().addingTimeInterval(1)
let deltaT : TimeInterval = 1.0
self.timer = Timer(fire: fire, interval: deltaT, repeats: true, block: { (t: Timer) in
print("hello")
})
RunLoop.main.add(self.timer!, forMode: RunLoopMode.commonModes)
}
Declare your timer as 'weak var' like this:
weak var timer: Timer?

How to make a Label disappear when button is pushed?

I am currently working on a very basic quiz app.
The user will have the option of 4 choices.
If the correct button is pressed, the hidden label (LabelEnd) will show "Correct" and automatically go to the next randomized question.
If they push the wrong button, the hidden label will then read "Wrong" and stay on the same question.
My question is: how do I make it so that the label is hidden again after 1 second, regardless if it is correct or wrong?
func Hide() {
LabelEnd.hidden = true
}
func UnHide(){
LabelEnd.hidden = false
}
#IBAction func Button1Action(sender: AnyObject) {
UnHide()
if(CorrectAnswer == "1"){
LabelEnd.text = "Correct!"
RandomQuestions()
} else {
LabelEnd.text = "Wrong"
}
}
You can use the UIView animate method with duration where you can set your interval time.
Like this:
UIView.animateWithDuration(0.1, animations: { () -> Void in
LabelEnd.hidden = false
})
You can hide your label after 1 second this way:
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.LabelEnd.hidden = true
})
And you can change time as per your need.
And you can use it this way:
func Hide() {
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.LabelEnd.hidden = true
})
}
#IBAction func Button1Action(sender: AnyObject) {
UnHide()
if(CorrectAnswer == "1"){
LabelEnd.text = "Correct!"
Hide()
}
else{
LabelEnd.text = "Wrong"
Hide()
}
}
One more way to do that is you can use timer for that as shown in below code:
var timer = NSTimer()
func Hide() {
LabelEnd.hidden = true
timer.invalidate() //You can remove timer here.
}
func UnHide(){
LabelEnd.hidden = false
}
#IBAction func Button1Action(sender: AnyObject) {
UnHide()
if(CorrectAnswer == "1"){
LabelEnd.text = "Correct!"
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("Hide"), userInfo: nil, repeats: false)
}
else{
LabelEnd.text = "Wrong"
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("Hide"), userInfo: nil, repeats: false)
}
}