Search code examples
iosxcodeswiftnstimercountdowntimer

Countdown with several decimal slots, using NSTimer in Swift


I want to make an app that has a timer starting at 10.0000000 for example, and I want it to countdown perfectly Here's my code so far:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var labelTime: UILabel!

    var counter = 10.0000000

    var labelValue: Double {
        get {
            return NSNumberFormatter().numberFromString(labelTime.text!)!.doubleValue
        }
        set {
            labelTime.text = "\(newValue)"
        }
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        labelValue = counter
        var timer = NSTimer.scheduledTimerWithTimeInterval(0.0000001, target: self, selector: ("update"), userInfo: nil, repeats: true)
    }

    func update(){
        labelValue -= 0.0000001
    }


}

What happens is that my countdown is really slow, it's just not working and it would take like 1 hour to get to 0 seconds, instead of just 10 seconds. Any ideas? What changes should I make to my code? Thanks


Solution

  • Timers are not super-accurate, and the resolution of NSTimer is about 1/50th of a second.

    Plus, the refresh rate of the iPhone screen is 60 frames/second, so it's totally pointless to run your timer any faster than that.

    Rather than trying to use a timer to decrement something every time it fires, create a timer that fires like 50 times a second, and have it use clock math to update the display based on the remaining time:

    var futureTime: NSTimeInterval 
    
    override func viewDidLoad() {
        super.viewDidLoad()
        labelValue = counter
    
        //FutureTime is a value 10 seconds in the future.
        futureTime = NSDate.timeIntervalSinceReferenceDate() + 10.0 
    
        var timer = NSTimer.scheduledTimerWithTimeInterval(
          0.02, 
          target: self, 
          selector: ("update:"), 
          userInfo: nil, 
          repeats: true)
    }
    
    func update(timer: NSTimer)
    {
      let timeRemaining = futureTime - NSDate.timeIntervalSinceReferenceDate()
      if timeRemaining > 0.0
      {
        label.text = String(format: "%.07f", timeRemaining)
      }
      else
      {
        timer.invalidate()
        //Force the label to 0.0000000 at the end
        label.text = String(format: "%.07f", 0.0)
      }
    }