Search code examples
iosswiftnstimer

How to make NSTimer show Hrs : Min : Sec


I'm trying to make a timer show the following - hours : minutes : seconds : milliseconds. I found a website tutorial showing how to do it, but it doesn't show hours. Here is the website: Simple-stopwatch

Here is the code it said to do:

var timer = NSTimer()
var startTime = NSTimeInterval()

func updateTime()
{
    var currentTime = NSDate.timeIntervalSinceReferenceDate()
    var elapsedTime : NSTimeInterval = currentTime - startTime

    let minutes = UInt8(elapsedTime / 60.0)
    elapsedTime -= (NSTimeInterval(minutes) * 60)

    let seconds = UInt8(elapsedTime)
    elapsedTime -= NSTimeInterval(seconds)

    let fraction = UInt8(elapsedTime * 100)

    let strMinutes = String(format: "%02d", minutes)
    let strSeconds = String(format: "%02d", seconds)
    let strFraction = String(format: "%02d", fraction)

    timeLabel.text = "\(strMinutes):\(strSeconds):\(strFraction)"
}

@IBAction func start(sender: AnyObject) {
    if !timer.valid {
        let aSelector : Selector = “updateTime”
        timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: aSelector, userInfo: nil, repeats: true)
        startTime = NSDate.timeIntervalSinceReferenceDate()
    }
}

@IBAction func stop(sender: AnyObject) {
    timer.invalidate()
}

I don't fully understand what going on in the updateTime() function. I don't get how it converts itself to min, secs... If someone can give me an explanation, I'd greatly appreciate.

My goal is to add hours, but I also want to completely understand whats going on.


Solution

  • I will guide you step by step on what you need to do.

    First couple of lines extract the time interval measured in seconds from the current time to the initial time:

    var currentTime = NSDate.timeIntervalSinceReferenceDate()
    var elapsedTime : NSTimeInterval = currentTime - startTime
    

    I assume these lines are obvious. Next, you want to calculate how many hours have passed.

    To do this you need to add:

    let hours = UInt8(elapsedTime / 3600.0)
    

    This line works because elapsedTime is measured in seconds. There are 3600 seconds in one hour. When you divide elapsedTime by 3600 you get the number of hours. However, this number can be fractional. For example, half hour corresponds to 1800 seconds and if you divide 1800 by 3600 you get 0.5. You need to round this down to get 0. For this you use UInt8() constructor. It rounds down numbers to the lowest integer. In my example UInt8(0.5) is equal to zero.

    You then use the same logic for other components noting that there are 60 seconds in one minute (which is why you are dividing by 60).

    You reduce the value of elapsedTime in between these calls:

    elapsedTime -= (NSTimeInterval(hours) * 3600)
    

    because you need to remove the number of seconds which corresponds to the number of hours from elapsedTime. Otherwise, when you get to calculating minutes, you will end up calculating a bigger amount. For example, if elapsedTime is equal to 3660 (1 hour and 1 minute) unless you deduct 3600 you will get 61 minutes.

    Overall, for your application the function would look like:

    func updateTime()
    {
        var currentTime = NSDate.timeIntervalSinceReferenceDate()
        var elapsedTime : NSTimeInterval = currentTime - startTime
    
        let hours = UInt8(elapsedTime / 3600.0)
        elapsedTime -= (NSTimeInterval(hours) * 3600)
    
        let minutes = UInt8(elapsedTime / 60.0)
        elapsedTime -= (NSTimeInterval(minutes) * 60)
    
        let seconds = UInt8(elapsedTime)
        elapsedTime -= NSTimeInterval(seconds)
    
        let fraction = UInt8(elapsedTime * 100)
    
        let strMinutes = String(format: "%02d", minutes)
        let strSeconds = String(format: "%02d", seconds)
        let strFraction = String(format: "%02d", fraction)
    
        timeLabel.text = "\(strMinutes):\(strSeconds):\(strFraction)"
    }
    

    More info about why deducting time is important

    Lets suppose that you have elapsedTime equal to 3660 second which is 1 hour and 1 minute.

    The line:

    let hours = UInt8(elapsedTime / 3600.0)
    

    will correctly calculate the number of hours as 1. The next line:

    elapsedTime -= (NSTimeInterval(hours) * 3600)
    

    deducts 1*3600 from the value of elapsedTime making it equal to 60. What this does is removes those seconds which should NOT be used for calculating number of minutes because they are being already represented by hours.

    Suppose you do not do this and you get to line:

    let minutes = UInt8(elapsedTime / 60.0)
    

    if your elapsedTime is still equal 3660 this line will make minutes equal to 61. You clearly do not want that!

    However, if you do deduct the number of hours, elapsedTime is equal to 60 and the line above will make number of minutes equal to 1. And the output will be 01:01 instead of 01:61.

    I hope this clarifies things.