Timer.fire() not working after invalidating in Swift - 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?

Related

Unable to stop the Timer by changing the Boolean value to false in Swift

I tried running the following code but the timer won't stop after counting till "0" seconds.
Desired OutPut : 5 seconds. 4 seconds. 3 seconds. 2 seconds. 1
seconds. 0 seconds.
class ViewController: UIViewController {
var seconds = 5
var value = true
#IBAction
func buttonPressed(_ sender: UIButton) {
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(secondsRemaining), userInfo: nil, repeats: value)
}
#objc
func secondsRemaining() {
print("\(seconds) seconds.")
if seconds > 0{
seconds -= 1
}else{
value = false
}
}
}
When you schedule a timer passing true as repeats, that means that this timer will continue to occur unless you explicitly call invalidate() function on the returning instance and set it to nil.
So, your code should be something like this:
class ViewController: UIViewController {
var seconds = 5
var timer: Timer?
#IBAction func buttonPressed(_ sender: UIButton) {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(secondsRemaining), userInfo: nil, repeats: true)
}
#objc func secondsRemaining() {
print("\(seconds) seconds.")
if seconds > 0 {
seconds -= 1
} else {
timer?.invalidate()
timer = nil
}
}
}

Why does my timer in swift keep speeding up?

I am creating a trivia app in swift and I have a timer that counts down each question. However as the user progresses with each question the timer speeds up. Can someone help me fix this?
My runGameTimer function:
func runGameTimer()
{
gameTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(RockTriviaViewController.updateGameTimer), userInfo: nil, repeats: true)
}
My updateGameTimer function:
#objc func updateGameTimer()
{
gameInt -= 1
timerLabel.text = String(gameInt)
if (gameInt == 0)
{
gameTimer.invalidate()
/*
if (currentQuestion != rockQuestions[questionSet].count)
{
newQuestion()
}
else
{
performSegue(withIdentifier: "showRockScore", sender: self)
}
*/
}
}
Where I call my code:
func newQuestion()
{
gameInt = 11
runGameTimer()
rockQuestion.text = rockQuestions[questionSet][currentQuestion]
rightAnswerPlacement = arc4random_uniform(3)+1
var Button: UIButton = UIButton()
var x = 1
for i in 1...3
{
Button = view.viewWithTag(i) as! UIButton
if(i == Int(rightAnswerPlacement))
{
Button.setTitle(rockAnswers[questionSet][currentQuestion][0], for: .normal)
}
else
{
Button.setTitle(rockAnswers[questionSet][currentQuestion][x], for: .normal)
x = 2
}
}
currentQuestion += 1
}
You're calling runGameTimer() in every call to newQuestion(). If a timer was already running then you'll add a new timer each time, and they will all call your selector. So if you have 3 timers running, your selector will be called 3x as often. That's not what you want.
Change your timer variable to be weak:
weak var gameTimer: Timer?
And then in runGameTimer invalidate the timer before creating a new one, using optional chaining:
func runGameTimer() {
gameTimer?.invalidate() //This will do nothing if gameTimer is nil.
//it will also cause the gameTimer to be nil since it's weak.
gameTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(RockTriviaViewController.updateGameTimer), userInfo: nil, repeats: true)
}
By making the game timer weak it will get set to nil as soon as it's invalidated. (When you schedule a timer the system retains it while it is running so it stays valid as long as it continues to run.)
By using optional chaining to reference the timer:
gameTimer?.invalidate()
The code doesn't do anything if gameTimer is nil.

Countdown speeds up when the button that starts it is pressed multiple times

I just started to learn swift . and I found when using a button to start a count down if i pressed the button twice it speeds up the process. What to add to prevent that?
#IBAction func startButton(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(processTime), userInfo: nil, repeats: true)
}
#objc func processTime(){
if counter > 0 {
counter -= 1
timeLabel.text = "\(counter)"
}else {
timer.invalidate()
}
}
I tried to use sender.isEnabled = false it gave this error (Value of type 'Any' has no member 'isEnabled')
so I did it like this :
#IBAction func startButton(_ sender: Any) {
if timer.isValid != true{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(processTime), userInfo: nil, repeats: true)
}
}
Many ways, depends on what logic you prefer.
Basically you need to ensure that the timer starts only once until it completes.
In the following example, we start the timer only if it's not been initialized previously.
Furthermore, when we stop the timer, we explicitly set it to nil so the following logic works again after the timer completes.
//globally declared as optional
var timer: Timer?
#IBAction func startButton(_ sender: Any) {
//check if timer is nil
if timer != nil {
//start timer only if timer is nil
timer = Timer.scheduledTimer(timeInterval: 1,
target: self,
selector: #selector(processTime),
userInfo: nil,
repeats: true)
}
}
#objc func processTime() {
if counter > 0 {
counter -= 1
timeLabel.text = "\(counter)"
}
else {
timer.invalidate()
//clear the timer
timer = nil
}
}
add a sender.isEnabled = false and after you press the button once it won't be clickable again
You need to invalidate the timer by calling timer.invalidate() before you reassign a new timer. If you assign a new timer instance to the old one without invalidating, you will lose reference to it and it will never be invalidated.

How to count Timer on Swift

I'm a newbie in swift and I tried to make a Timer. It should normally count the seconds and print them in the debugger.
I tried this:
var timer = Timer()
#IBAction func killTimer(_ sender: AnyObject) {
timer.invalidate()
}
#objc func processTimer() {
print("This is a second")
}
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
}
I don't know how the timer count seconds.. With this code i get an fail message:
#objc func processTimer() {
print("This is second \(Timer + 1)")
}
Thanks for your help.
A
You need a counter variable which is incremented every time the timer fires.
Declare the variable Timer as optional to invalidate the timer reliably (only once).
var timer : Timer?
var counter = 0
#IBAction func killTimer(_ sender: AnyObject) {
timer?.invalidate()
timer = nil
}
#objc func prozessTimer() {
counter += 1
print("This is a second ", counter)
}
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval:1, target:self, selector:#selector(prozessTimer), userInfo: nil, repeats: true)
}
You need to start your timer, you have only initialised it in your code so in viewDidLoad add
timer.fire()
I am not sure what you want to print but if it is a timestamp then you could do add a property to your class
let formatter = DateFormatter()
and configure it to show time in seconds and milliseconds
formatter.dateFormat = "ss.SSS" //in viewDidLoad
and use it in your print statement
print("This is a second \(formatter.string(from(Date())")
If you want to print the time that passed use this (If that's what you want):
print("time passed: \(Date().timeIntervalSince1970 - timer.fireDate.timeIntervalSince1970)")

"if" statement, condition will never be executed

I thought I was good with if statements but apparently Im still missing something. In the line timer.invalidate() the compiler complains
Will never be executed
As far as I can tell my syntax is correct.
#IBAction func button(_ sender: Any) {
let timer = Timer.scheduledTimer(timeInterval:0.2 , target: self, selector: #selector(ViewController.imageSwitch) , userInfo: nil, repeats: true)
var buttonState = false
if buttonState == true {
timer.invalidate()
buttonState = false
}
if buttonState == false {
timer.fire()
buttonState = true
}
}
You have to move the declaration of buttonState out of the method. And then simplify the if conditions:
var buttonState = false
#IBAction func button(_ sender: Any) {
let timer = Timer.scheduledTimer(timeInterval:0.2 , target: self, selector: #selector(ViewController.imageSwitch) , userInfo: nil, repeats: true)
if buttonState {
timer.invalidate()
buttonState = false
} else {
timer.fire()
buttonState = true
}
}
You should additionally ask yourself what the methods is even supposed to do, do you want the buttonState to continously switch from true to false and back or do you want it to do something else (like switching to to another value after a delay)? In the first case you should declare the timer outside the method and let it run there, never invalidate it - in the second case you should move the timer declaration inside the else block and turn repeat off. In both cases you should probably remove the timer.invalidate() and timer.fire() calls.
Full solution :
var timerState = false
var timer = Timer()
#IBAction func button(_ sender: Any) {
if timerState == false {
timer.invalidate()
timer = Timer.scheduledTimer(timeInterval:0.2 , target: self, selector: #selector(ViewController.imageSwitch) , userInfo: nil, repeats: true)
timerState = true
} else {
timer.invalidate()
timerState = false