2

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.

  • Have you tried debugging? any info you can pass along from there? e.g. do you ever actually hit a breakpoint inside the updateElapsedTimeLabel method? if you do then you can verify that the timer/selector is working properly. – Jacob Lange Apr 16 '18 at 23:47
  • Yeah tried everything, I get absolutely nothing! It's well strange –  Apr 16 '18 at 23:50
  • Have you tried starting the watch before the timer? – Jacob Lange Apr 16 '18 at 23:57
  • What do you mean, the watch is the timer –  Apr 16 '18 at 23:59
  • I meant swapping the two lines of code inside the start button. just to guarantee that the first time the Timer.scheduleTimer fires the watch is running. probably unlikely it wouldn't be but figured it might be worth trying. – Jacob Lange Apr 17 '18 at 00:02
  • yeah, same effect, nothing happens –  Apr 17 '18 at 00:03

1 Answers1

2

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()
        }
    }
}
Code Different
  • 90,614
  • 16
  • 144
  • 163