Whether it’s as subtle as a hamburger button or as significant as a brand new user interface, animations have been one of the strongholds of iOS App development. A major reason for this is that due to the legwork Apple have put into their UIKit and CoreAnimation libraries, adding these animations has been cheap and easy to implement by developers1. With parameters to control properties of the animation (e.g. duration, easing, delay, repeat count, auto reverse) as well as many Apple-provided controls that are animatable out-of-the-box (e.g. frame, background colour, alpha, transform), we have been truly spoiled. How could any of us now work on a platform that wasn’t as generous as this?

UPDATE: some of the content in the page didn’t survive a migrating from my old blog platform to my new blog platform. As the content of this post is obsolete now (since watchOS 2, I won’t bother fixing the missing content. The rest is here for posterity.

Unfortunately, it seems like WATCH is raining pretty hard on our parade when it comes to animations. Due to the restrictive nature of the device (both in screen real-estate and battery life) there are no UIViews or CALayers to tinker with, meaning there are no dynamic animation APIs. Instead, we’ve been thrown back in time to the early days of animation where we must essentially create flip books. We still get some limited control over the animation, but nothing like we had before:

watch

That’s right - we can decide if a WKInterfaceImage (which is WatchKit’s answer to UIImageView) is animated, and if so, how long does that animation last. If it is animated, it will look for sequentially-named files in your bundle (e.g. frame0.png, frame1.png, frame2.png, … ) and play through them on repeat.

Recreating WATCH’s Activity Indicator

The problem here for me is that I’m so used to getting Apple to do the legwork, (e.g. handling the animation curve and tweening between keyframes) that I’ve no expertise in creating flip books. If I were to create a 150 frame animation, and decide that I wanted to tweak it to be a little bouncier, I don’t want to spend time adjusting dozens of frames.

Consider something simple like the activity indicator on WATCH2:

[Content removed]

I really like the cuteness of this spinner - notice how each ball scales up and fades in like they are a fuse kicking off the process you’re waiting for. As each ball gains momentum, so does the whole group, and the spinner speeds up. I also love the illusion that the balls are slightly squashed, like petals a flower, when in fact, they are all perfect circles.

Use iPhone to Capture Frames

As I mentioned, my only experience of animation in the last 5 years has been mostly with iOS, so I’m really in the dark about what software is out there that could help me. Also, it’s unlikely that any third party software will have the same feel as a native library (e.g. will the springiness match that of the device, what about the default animation curve). If we could use UIKit and Core Animation, we’d get all this for free… so why don’t we just do that? Obviously for this to be useful, the resulting animation needs to be 60fps, so let’s look into that later.

To begin, I created the animation and placed it in an iPhone App; here’s what it looks like running on the iPhone Simulator.

[Content removed]

Next I created the UIView Recorder - it’s a simple class that contains a CADisplayLink and some image capturing/export code. It starts recording the activity indicator view3 when we startAnimating, and stops recording when we stopAnimating.

Once it stops, we are then presented with some output:

  • Recorded: 145 images
  • Duration: 2.41957300901413 seconds
  • Frames stored in: ~/Library/Developer/CoreSimulator/Devices/2520DD5C-03FA-4894-A99F-9BCF5C07BDE5/data/Containers/Data/Application/63F724D7-67FA-4D06-9993-35BF475861B0/Documents/

You can see that we’re getting 145/2.42 = 60 frames per second.

Here are the first 28 of the 145 images that were produced - note that the background colour has been added (by CSS) so you can see the foreground; the PNG has a transparent background.

[Content removed]

Now all that remains is to add these images to the Images.xcassets of my WatchApp, and add a WKInterfaceImage that references them. Also, as I won’t want this to repeat forever, I’ll add an IBOutlet to the InterfaceController and set:

	@IBOutlet weak var image: WKInterfaceImage!

    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)
        image.startAnimatingWithImagesInRange(NSMakeRange(1, 145), duration: 2.41, repeatCount: 1)
    }

Below you can see Apple’s spinner as my App launches and then mine - I’ve coloured mine red to help you identify which spinner is which.

[Content removed]

If the animations look slow or stuttery, please trust me that they are 60fps when running on the device.

Summary

  1. Create your animations as you have been doing in an iPhone App
  2. Use the UIView Recorder to record the animation and export the frames as PNG or JPG.
  3. Import the frames into your watch app

I’m still very much learning about WatchKit, so I may very well post an update to this method shortly. Until then, I would love to hear if you have any better ideas, so please let me know in the comments below.

  1. In the days of iOS 3.0, I would often comment on how confusing development must be to outsiders because performing multiple transforms, perspective changes or shadows animations would be about 5 minutes work, whereas dynamically underlining a word of text in a paragraph would take hours. 

  2. Interesetingly, developers are not currently able to display or hide the WATCH activity indicator at will. 

  3. The recorder doesn’t capture the background, so we get a nice output with transparency.