Search code examples
swifttimernsdatenstimernstimeinterval

Game Timer using NSDate with NSTimer? swift


I want to start a timer at a specific date and time, then use that start time as a game timer for the rest of the game. Using "timeIntervalSinceDate" will give me seconds but then trying to get the seconds to display on the gameTimerLabel won't work. I might be coming at this the wrong way. Any advice is welcome.

override func viewWillAppear(animated: Bool) {
    let dateFormatter = NSDateFormatter()


    dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
    let dateAsString1 = "Fri, 1 April 2016 11:30:00 MST"
    let date1 = dateFormatter.dateFromString(dateAsString1)!
    var currentTime = NSDate()
   var counter = 0
    gameTimerLabel.text = String(counter)
    gameTimerLabel.text = counter  //<- Error:Cannot assign value to type 'Int' to type 'String?'
    counter = date1.timeIntervalSinceDate(currentTime)  //<- Error:Cannot assign value of type 'NSTimeInterval' (aka 'Double') to type 'Int'

}

Solution

  • A couple of things here

    First, when you declare counter, it's inferred to be of type Int

    var counter = 0
    

    You can declare it as a double by adding a .0 or specifying it's type:

    var counter: NSTimeInternval = 0.0
    

    Next, you can use string interoperability to display the count variable in a string, like this:

    gameTimerLabel.text = "\(counter)"
    

    Here's an example view controller using an NSTimer as a counter, it counts in seconds:

    class ViewController: UIViewController {
    
    // Setup a timer and a counter for use
    var timer   = NSTimer()
    var counter : NSTimeInterval = 0
    
    @IBOutlet weak var label: UILabel!
    
    // Invalidest the timer, resets the counter and udpates the label
    @IBAction func resetTimer(sender: AnyObject) {
    
        // Invalidate the timer, reset the label.
        self.timer.invalidate()
        self.label.text = ""
        self.counter    = 0
    }
    
    // A button that when pressed starts and stops the timer
    // There's no pause/resume, so it's invalidated & created again
    // But the counter value reamins the same
    @IBAction func timerBttnTouched(sender: AnyObject) {
    
        if self.timer.valid {
    
            self.timer.invalidate()
    
        } else {
    
            self.setupTimer()
        }
    }
    
    // Does the actual counting everytime the timer calls this method
    func timerFired() {
    
        self.counter += 1
        self.label.text = "\(self.counter)"
    }
    
    // Setups a timer, adds it to the run loop and specifies what method should fire when the timer fires
    func setupTimer() {
    
        // Setupt the timer, this will call the timerFired method every second
        self.timer = NSTimer(
            timeInterval: 1,
            target: self,
            selector: #selector(self.timerFired),
            userInfo: nil,
            repeats: true)
    
        // Add the timer to the run loop
        NSRunLoop.currentRunLoop().addTimer(
            self.timer,
            forMode: NSDefaultRunLoopMode)
    }
    }
    

    An important thing to note when using timers is they may not always be called exactly when you need them to, this should be taken into account according to your desired precision when using a timer.

    As discussed in comments, here's the solution using a timer to fire a method that compares two dates and uses a NSDateComponentsFormatter to generate a string for display. The initial date is generated in viewDidLoad but can be created anywhere:

    class ViewController: UIViewController {
    
    // Setup a timer and a counter for use
    var timer   = NSTimer()
    var counter : NSTimeInterval = 0
    
    var startDate: NSDate?
    
    override func viewDidLoad() {
    
        // Set the initial date
        self.startDate = NSDate()
    }
    
    @IBOutlet weak var label: UILabel!
    
    // Invalidest the timer, resets the counter and udpates the label
    @IBAction func resetTimer(sender: AnyObject) {
    
        // Invalidate the timer, reset the label.
        self.timer.invalidate()
        self.label.text = ""
        self.counter    = 0
    }
    
    // A button that when pressed starts and stops the timer
    // There's no pause/resume, so it's invalidated & created again
    // But the counter value reamins the same
    @IBAction func timerBttnTouched(sender: AnyObject) {
    
        if self.timer.valid {
    
            self.timer.invalidate()
    
        } else {
    
            self.setupTimer()
        }
    }
    
    // Does the actual counting everytime the timer calls this method
    func timerFired() {
    
        let now = NSDate()
        let difference = now.timeIntervalSinceDate(self.startDate!)
    
        // Format the difference for display
        // For example, minutes & seconds
        let dateComponentsFormatter = NSDateComponentsFormatter()
        dateComponentsFormatter.stringFromTimeInterval(difference)
    
        self.label.text = dateComponentsFormatter.stringFromTimeInterval(difference)
    }
    
    // Setups a timer, adds it to the run loop and specifies what method should fire when the timer fires
    func setupTimer() {
    
        // Setupt the timer, this will call the timerFired method every second
        self.timer = NSTimer(
            timeInterval: 1,
            target: self,
            selector: #selector(self.timerFired),
            userInfo: nil,
            repeats: true)
    
        // Add the timer to the run loop
        NSRunLoop.currentRunLoop().addTimer(
            self.timer,
            forMode: NSDefaultRunLoopMode)
    }
    }