Search code examples
iosswiftstopwatchelapsedtime

Elapsed Timer not starting


I am building an elapsed timer and while the code gives no errors the timer does not start.

I am using two ViewControllers, one called Stopwatch which has the start stop function in it under the class Stopwatch() and then a regular ViewController with the rest in it.

Main Storyboard Button and Outlet Connections

View Controller Code:

import UIKit

class ViewController: UIViewController {

let watch = Stopwatch()

@IBOutlet weak var elapsedTimeLabel: UILabel!

@IBAction func startButton(_ sender: Any) {
    Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.updateElapsedTimeLabel), userInfo: nil, repeats: true)

    watch.start()
}

@IBAction func stopButton(_ sender: Any) {
    watch.stop()
}

@objc func updateElapsedTimeLabel (timer : Timer) {
    if watch.isRunning {
        let minutes = Int (watch.elapsedTime/60)
        let seconds = watch.elapsedTime.truncatingRemainder(dividingBy: 60)
        let tenOfSeconds = (watch.elapsedTime * 10).truncatingRemainder(dividingBy: 10)
        elapsedTimeLabel.text = String (format: "%02d:%02d:%02d", minutes, seconds, tenOfSeconds)
    } else {
        timer.invalidate()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

override var prefersStatusBarHidden: Bool {
    return true
}
}

The Stopwatch View Controller code:

import Foundation

class Stopwatch {

private var startTime : Date?

var elapsedTime: TimeInterval {
    if let startTime = self.startTime {
        return -startTime.timeIntervalSinceNow
    } else {
        return 0
    }
}
var isRunning: Bool {
    return startTime != nil
}

func start() {
    startTime = Date()
}

func stop() {
    startTime = nil
}
}

There is nothing at all coming in the debug window, so not sure what the issue is, I reconnected the buttons over and over so it's not that. I also get no other errors in the code as mentioned above.

Can anyone shed some light on this. Maybe I am using the wrong #selector or I am doing the updateElapsedTimeLabel minutes, seconds, tenOfSeconds calculations wrong. Not sure. Thanks for having a look.


Solution

  • If you Option-click on seconds and tenOfSeconds you will find that one is of type TimeInterval (i.e. Double) and the other is of type Double. So your format specifier of %02d was wrong. In C, a mismatch between the format specifier and the argument is undefined behavior. Swift doesn't say how it handles that but I guess it will ignore the argument.

    To fix it, change your format specifier for the last 2 components to %02.f:

    let minutes = Int(watch.elapsedTime/60)
    let seconds = watch.elapsedTime.truncatingRemainder(dividingBy: 60)
    let tenOfSeconds = (watch.elapsedTime * 100).truncatingRemainder(dividingBy: 100) // fixed the math here
    elapsedTimeLabel.text = String(format: "%02d:%02.f:%02.f", minutes, seconds, tenOfSeconds)
    

    But why not use a DateFormatter to make your life simpler:

    class ViewController: UIViewController {
        private let formatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "mm:ss:SS"
            return formatter
        }()
    
        @objc func updateElapsedTimeLabel (timer : Timer) {
            if watch.isRunning {
                elapsedTimeLabel.text = formatter.string(from: Date(timeIntervalSince1970: watch.elapsedTime))
            } else {
                timer.invalidate()
            }
        }
    }