4.2 Make a Countdown Display With `WKInterfaceTimer`
We want to show the user a countdown for each of the stages that make up a reminder. To do this, we may want to transition from a basic label to a slightly more functional control. That is where the
WKInterfaceTimer comes into play.
1.Introduction2 lessons, 05:00
2.Building the Foundation3 lessons, 27:38
3.Creating the User Interface7 lessons, 1:06:19
4.Application Logic6 lessons, 37:10
5.Conclusion1 lesson, 00:38
4.2 Make a Countdown Display With `WKInterfaceTimer`
It's time to start bringing the concept of time into our application,. And we've kind of avoided it to this point, but we can't really get any further without having some sort of mechanism to incorporate a timer, or something along those lines. To know when the end of a stage has come or when the end of the reminder or the total reminder is done in and of itself. So let's start to add that concept in. So what I wanna do is, I wanna head back over to our interface story board. And come back to our reminder interface controller and this stage duration label, we need to get rid of it. Now you could use a label and you could update it programmatically behind the scenes, but that tends to be a little bit more work and the nice thing is that there already is a timer built into the WatchKit Framework. So if I start to search for timer, you're going to see that I have this interfaceTimer right here. So that's actually what I'm gonna use instead. So with my stage duration selected, I'm actually going to delete that and I'm gonna grab this timer and I'm gonna drag it into that center interface. So let's make sure it's in that middle group. As you can see here, I get an interface kinda sample already. So you can see this says, that this is 0 hours 59 minutes and 59 seconds and you can adjust this and what it looks like. So there's a couple different formats you can use, positional is the one that we're going to use. But you can use different ones like abbreviated or short or you can pick whichever one you really want, but I happen to like the positional. Then you can specify what type of units you're looking at. Do you wanna use hours, minutes, and seconds? Do you wanna add in days? You could go on and on and on and depending on how long these stages could be, you could obviously check and use different units. But I think this is gonna be fine for now. So what I wanna do is now that we have this piece of UI in here, I wanna get programmatic access to that. So let's go ahead and save these changes. Then, we're going to go into our assistant editor like we've done so many times before, and I wanna make sure that I have the proper interface controller here. And where we left this little comment is for our stage duration. Let's go ahead and now control-click and drag over here and create a new outlet. And this one, we're gonna call our interfaceTimer this is gonna be a WKInterfaceTimer and Week will be fine. So let's go ahead and connect that. So what I can do now, is I can start to use this timer to countdown to a particular time. So if I wanna start off with a stage that happens to be 20 seconds, maybe I wanna do a count down and start from 20 and count it down to 0. So that's kinda where we wanna get to for this particular lesson. So what I wanna do is, let's go back to our standard editor now and go into our reminder controller. So what I wanna do is when we actually get to a point where we want to be running our stage, I wanna show that in the interface. But in order to properly do that, I'd like to create a little helper function here first. That's going to kinda set up that interfaceTimer and get it moving. So what I would like to do is create another private function. And we're just gonna call this interfaceTimerReset, how's that sound? Lets pass a couple of things in here, we're gonna pass in our WKInterfaceTimer and we're also going to pass in an interval or a time interval as a matter of fact, TimeInterval. So now I could pass in WKInterfaceTimer and it can also pass in the interval that I wanna reset it for to set everything up, to have it display the proper time, and then to start the timer so it actually starts to move. So at this point, we could assume that this timer's already running, so if we're resetting it, then we're gonna want to take our timer and we're gonna want to stop it first. And then, we need to reset the time, so we need to give it a start timer, a start duration. And that needs to be in a date format, so we'll say let time be equal to date. And we're gonna pass into this a TimeInterval, and we're gonna use the SinceNow variation of this function. And the interval is gonna be exactly what we passed in, so we're gonna create a time that is of a certain interval amount of time since right now. Now that we have that date, we can then say to our timer, we want to set the date be equal to that time. And then, I can say timer.start and save that. Okay, so now we have our interfaceTimer all setup but now we need to call this interfaceTimerReset to get things going. And that, at the very beginning of this particular view, is going to be within our start function. So when we actually begin the process of starting this particular stage. So when I click on the action button clicked or when I click on that button and I'm in the ready state, I'm gonna run start. And so what do I wanna do at this particular point in time? Well, the first thing that I wanna do is I want to reset my state and I wanna say now I'm going to be in the running state. Then, I'm gonna change the action button text, so I'm gonna say setTitle. And at this point, I can transition to the end stage, so I'll just say End Stage like this. And then I need to create that interval so that I can call my interfaceTimerReset. So I'm gonna say let interval be equal to, and I'm gonna create a new TimeInterval that is going to be exactly, and I need to get my active stage, which I have set up here. I'm gonna get my active stage and I wanna get the seconds that are involved in it, so I can say activeStage.seconds. Now this is going to complain a little bit because this active stage is actually an optional, so it's going to do some unwrapping for me so I will let it do that. So now I have my interval, now I can call my interfaceTimerReset function, and I can pass in my interfaceTimer as well as my interval. So we'll go ahead and save that. But one thing that I wanna do before we actually get everything started here, and I'm going to have to unwrap this as well. One thing that I wanna do at this point is when I save or when I set my active stage, I want to set that default timer on there so that it can actually display it to the end user before everything is running. And it's gonna be a similar process, so we'll just say let interval be equal to a TimeInterval. Once again, we will be using the exactly version and we'll just let it figure out which one it needs. And once again, we're gonna get the activestage.seconds, and it's going to want to unwrap this again for me, so I will allow it to. So now I have this interval, and then we'll go ahead and say let date be equal to Date and this date is gonna be timeIntervalSinceNow. We'll say interval, and then once again we'll say InterfaceTimer.setDate. Now you could definitely extract this into a little helper function, that would be fine because we could reuse it in other places. But for now, I will leave as an exercise for you to take care of. And it will pass in this date into our set date function, let's go ahead and save that. So now at this point, I would like to run my application. And I would like to see it show up, not only giving me the initial stage as the text name for the stage on my UI. But I would also like to see that interfaceTimer there ready to go, as well as being able to see the timer going down once it's actually started. So let's go ahead and run our application. Now our Apple watch simulator comes up, let's go ahead and click the morning wake-up. And as you can see now, we have our stretch as our first stage but the timer here is actually a little misleading because it says 19 here. And the reason that it says that, even though our stage duration is 20 seconds, is because the timer itself is zero based. So when it does the calculation, it counts the 0 second on its interfaceTimer as an actual second, so it's gonna go from 19 down to 0. So now if I wanted to actually start it, I could click my button and you see the countdown beginning. And the interesting thing about the interfaceTimer, which is also one of its shortcomings, is that it will go all the way down to zero, but there's really no events or anything happening once it gets down to zero. So how do we know when things have actually ended? And that's one of the more interesting aspects of the interface controller. And the way that we're gonna handle that, is we're going to introduce a secondary timer to help get those types of events in the next lesson.