Tuesday, April 19, 2016

Watch tutorial 4: Animation

This post is part of a set of short tutorials on Watch. If you want to see the previous post. In this tutorial, you're going to create ring animation that looks like the one in the Activity app.



The different types of animations

Like we've seen with Layout, animations on the AppleWatch are very simple. There are two main types of animations in WatchKit: property animations and animated images.
  • Properties animation is limited to some properties of of UI elements like: width/height, alpha, background color, inset.
  • Animated images is simply a set of images. When you run them quickly, your eyes see them as animated: the basic of cartoon animation :)

Build your images

When I first started on AppleWatch, I searched for this cool ring (that is used in Activity app) in the UI control list without success. It is not part of the start ui controls. You can do such animation but you've got to build your own imagines.

After googling, I found this interesting project: RadialChartImageGenerator which also comes with some online tooling available to generate the images. Ah the joy of open source! Let's use the tool to generate our images.

  • Go to RadialChartImageGenerator online tool
  • Select the single Arc
  • For Current and Max value select 90
  • Select the color that match task color. You can start with dark blue and go clearer. (See image below for color reference)
  • At the bottom, untick Show Text and Subtext. You only need a empty ring set of images. As you deal with timer programmatically.
  • Hit Generate Images button, the images are downloaded in you download folder


Get starter project

In case you missed Watch tutorial 3: Layout, here are the instructions how to get the starter project. Clone and get the initial project by running:
git clone https://github.com/corinnekrych/DoItCoach.git
cd DoItCoach
git checkout step3
open DoItCoach.xcodeproj

Add images to Xcode project

From the previous tutorial, you should have the animation images already populated in HOME_DIR/DoItCoach/DoItCoach WatchKit App/Assets.xcassets folder.

Optionally if you want to add your own ring generated images:
  • Create a new folder in Asserts.xcassets, name it TimeSpent,
  • copy your images here. You should get something like this.

Animate your images

You can animate all object that conform to WKImageAnimatable.
You are going to animate the background image of the main Group.

In DoItCoach WachKit Extension, go to InterfaceController.swift, go to the onStartButton and add the following 2 lines of code:
group.setBackgroundImageNamed("Time")
group.startAnimatingWithImagesInRange(NSMakeRange(0, 90), duration: currentActivity.duration, repeatCount: 1)
The final method should look like:
@IBAction func onStartButton() {
  guard let currentTask = TasksManager.instance.currentTask else {return}
  if !currentTask.isStarted() {
    let duration = NSDate(timeIntervalSinceNow: currentTask.duration)
    timer.setDate(duration)
    timer.start()
    currentTask.start()
    group.setBackgroundImageNamed("Time")
    group.startAnimatingWithImagesInRange(NSMakeRange(0, 90), duration: currentTask.duration, repeatCount: 1)
    startButtonImage.setHidden(true)
    timer.setHidden(false)
    taskNameLabel.setText(currentTask.name)
  }
}
Well done! You get your timer animation done!

Go to next task

Once the timer is fired, your Watch should display the next task on the list.

The UI Timer object does not provide a fire method. So to trigger an event once the the task is done, you have to add a NSTimer object.
@IBAction func onStartButton() {
   guard let currentTask = TasksManager.instance.currentTask else {return} 
   if !currentTask.isStarted() { 
      let duration = NSDate(timeIntervalSinceNow: currentTask.duration)
      timer.setDate(duration)
      // Timer to fire event
      NSTimer.scheduledTimerWithTimeInterval(currentTask.duration,
                                             target: self,
                                             selector: #selector(NSTimer.fire),
                                             userInfo: nil,
                                             repeats: false) // [2]
      timer.start() 
      // Animate
      group.setBackgroundImageNamed("Time")
      group.startAnimatingWithImagesInRange(NSMakeRange(0, 90), duration: currentTask.duration, repeatCount: 1)
      currentTask.start()
      startButtonImage.setHidden(true) 
      timer.setHidden(false) 
      taskNameLabel.setText(currentTask.name)
   }
}
func fire() {  // [2]
  timer.stop()
  startButtonImage.setHidden(false)
  timer.setHidden(true)
  guard let current = TasksManager.instance.currentTask else {return}
  current.stop()
  group.stopAnimating()
  display(TasksManager.instance.currentTask)
}

func display(task: Task?) {
  guard let task = task else {
    taskNameLabel.setText("NOTHING TO DO :)")
    timer.setHidden(true)
    startButtonImage.setHidden(true)
    return
  }
  group.setBackgroundImageNamed("Time0") // [3]
  taskNameLabel.setText(task.name)
}
  • [1]: start an timer that is used to refresh UI display. Note the the Timer UI component can not be associated to a fires method.
  • [2]: in the fire method, you need to display start button, hide timer info, stop the animation and display the next task.
  • [3]: to initialise the Group background image to the initial image
Hooray, your timer starts, animate and go to the next task! Victory.

Animation and Watch App life cycle

For the need of testing DoItCoach, we made the timer duration to 10 seconds. In real life, the timer would be of 25 mins. Your AppleWatch won't say in foreground the whole duration of the task. You need to take care of replaying the animation in willActivate() in InterfaceController.swift. You will have to calculate the remaining time and make it match the image number for your animation. This is your challenge!

Get final project

If you want to check the final project, here are the instructions how to get it.
cd DoItCoach
git checkout step4
open DoItCoach.xcodeproj

What's next?

With this first introduction tutorial, you saw how you can do animation that look like the Activity app on your AppleWatch app. In the iOS app we can also see task in progress by selection the task in the table view. what about if the task has been started with the watch. wouldn't be nice to see it running on both AppleWatch and iOs app?

This is time to talk about WatchConnectivy! See Watch tutorial 5: Watch Connectivity (Direct Message)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.