3

I made a practice project in Swift to learn how NSTimer works. There is one button to start the timer and one button to invalidate it. It works fine when I tap each button once. However, when I tap the start timer button multiple times, I am no longer able to invalidate it.

enter image description here

Here is my code:

class ViewController: UIViewController {

    var counter = 0
    var timer = NSTimer()

    @IBOutlet weak var label: UILabel!

    @IBAction func startTimerButtonTapped(sender: UIButton) {
        timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
    }

    @IBAction func cancelTimerButtonTapped(sender: UIButton) {
        timer.invalidate()
    }

    func update() {
        ++counter
        label.text = "\(counter)"
    }
}

I have seen these questions but I wasn't able to glean an answer to my question from them (many are old Obj-C pre-ARC days and others are different issues):

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • 2
    Timer is not like other common object,if you call this `scheduledTimerWithTimeInterval ` you add a new timer to Runloop,when you call `invalidate `,you remove it from Runloop.So,if you `scheduledTimerWithTimeInterval` one time,you need to `invalidate` one time – Leo Dec 04 '15 at 12:10

2 Answers2

8

You can add timer.invalidate() before starting a new timer in startTimerButtonTapped if you want to reset the timer each time the "start" button is tapped:

@IBAction func startTimerButtonTapped(sender: UIButton) {
    timer.invalidate()
    timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}

I was going to update with an explanation but @jcaron already did it in the comment, so I'm just quoting his text, no need to change it:

Every time you tap on the "Start Timer" button, you create a new timer, while leaving the previous one running, but with no reference to it (since you've overwritten timer with the new timer you just created). You need to invalidate the previous one before you create the new one.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • Nice. This works. Do you know what is going on behind the scenes that won't allow multiple timer starts to be invalidated? – Suragch Dec 04 '15 at 12:02
  • 2
    @Suragch, every time you tap on the "Start Timer" button, you create a new timer, while leaving the previous one running, but with no reference to it (since you've overwritten `timer` with the new timer you just created). You need to invalidate the previous one before you create the new one. – jcaron Dec 04 '15 at 12:05
0

I would like to suggest you to set timer to nil when press on cancel button. And don't forget to set counter =0 When invalidating the Timer.

Devang Tandel
  • 2,988
  • 1
  • 21
  • 43
  • Since `timer` is not an optional, it is not currently possible to set it to nil. Are you suggesting that it should be an optional? What difference would that make from just invalidating it every time? – Suragch Dec 05 '15 at 00:41
  • sorry, i just saw the screen you have posted, there is nothing wrong with the timer. There is a bug in you code from your end. func update() { ++counter label.text = "\(counter)" } this function will increment the counter value on every tick,The counter get incremented on every tick regardless of which timer you have fired, just set counter=0 @IBAction func startTimerButtonTapped(sender: UIButton) { counter=0 timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true) } – Devang Tandel Dec 07 '15 at 05:14
  • For my purposes in the above example I didn't need the `counter` variable to be reset to 0, but I can understand how that would be desirable for many applications. I was more wondering about the necessity of setting the timer itself to nil. – Suragch Dec 07 '15 at 07:36