In this tutorial we’re going to use the Apple Watch animation feature of WKInterfaceImage
. To be sure, this is not an introductory tutorial to creating your first Apple Watch app. I’m going to assume you know how to create an Apple Watch target in your project, run a watch app, etc. If you don’t then I suggest you review these fine tutorials first:
- Apple WatchKit Programming Guide
- WatchKit Initial Impressions
- Getting Started With WatchKit
- Build an Apple Watch App for Location Aware Weather
We’re going to animate a classic Muybridge sequence of a galloping horse:
As Apple Watch animations are based upon sequencing through a set of images, our first steps here are to take the GIF image and split it into its constituent frames and then convert the frames to PNG files. Two tools were used for this: an online animated GIF image splitter and ImageMagick. If you want to follow along with the tutorial and don’t want to bother with splitting up the animated GIF, converting to PNGs, etc. you can download the sequence here.
Drag and drop the split image files into the watch Images.xcassets folder (not the watchkit extension Images.xcassets). Note that the images are all named with the pattern frame-n@2x.png
where n increments from 0 to 15. We could have dropped the dash out of the filename and just had framen@2x.png
but I prefer the dash for readability.
You should have something similar to the following in Xcode:
Now, let’s move over to Interface.storyboard of our watch app and drag and drop an Image and Slider onto the interface. Set the dimensions of the image to 150 width by 100 height. For the slider, set the initial value to 0.5 and the minimum and maximum value to 0 and 1, respectively. Set the Steps to 10 and leave the Continuous setting unchecked. This provides us with a slider with a range of 0.0 to 1.0 in 0.1 increments.
Connect your image to InterfaceController.swift
as an outlet and name it horseImage
. Then connect an action from the slider and name it sliderTapped
. Note that there is a bug in the latest beta of Xcode 6.2 that will result in an immediate error Argument to 'IBAction' method cannot have non-object type 'Float'
. To eliminate this error just delete the @IBaction
annotation from the line. Your interface should look like this:
On to InterfaceController
! In your InterfaceController
init
routine add the code to set the base image name of WKInterfaceImage
. iOS will recognize the naming pattern and set the first image frame as frame-0@2x.png
. Then, use the startAnimatingWithImagesInRange
method to begin animating the image sequence.
startAnimatingWithImagesInRange
takes three arguments: imageRange
, duration
, and repeatCount
. imageRange
is of type NSRange
, which is little more than a struct
to keep track of a starting point and length. For our purposes we want to animate through all of our images so our NSRange
is constructed with NSMakeRange(0, 15)
which indicates to start with image 0 and continue through 15 images (or, frame-14@2x.png
).
The duration
parameter specifies the total amount of time it will take to go through one loop of the sequence. If you’re used to working with frames per second then it would be given by the number of frames in your sequence divided by the duration. We’ll start off with 15 frames per second, and since we have 15 frames, that would give us a duration of 1.
The repeatCount
determines how many animation loops will be performed. We want to loop continuously so we set it to -1
.
1 2 3 4 5 6 |
override init(context: AnyObject?) { super.init(context: context) horseImage.setImageNamed("frame-") horseImage.startAnimatingWithImagesInRange(NSMakeRange(0, 15), duration: 1, repeatCount: -1) } |
Using advanced mathematic formulas used to put the space shuttle in orbit we can increase or decrease the speed of our horse when the slider widget controls are tapped. Tapping the + control will speed up our horse by decreasing the duration of the animation sequence (that is, the frames per second display goes up) and tapping – decreases the speed by, you guessed it, increasing the animation duration.
Here’s our sliderTapped
function.
1 2 3 4 |
func sliderTapped(value: Float) { let duration:NSTimeInterval = -1*Double(value) + 1.5 horseImage.startAnimatingWithImagesInRange(NSMakeRange(0, 15), duration: duration, repeatCount: -1) } |
Again, as the slider goes up the duration decreases (the negative slope of the formula) and vice-versa.
One thing to notice here is that our animation loop will restart at the first frame whenever startAnimatingWithImagesInRange
is called. This is unavoidable as the watch is not telling us what frame is currently displayed. Fortunately for us it’s not too annoying.
And here’s the final product:
[youtube]http://youtu.be/b3DuDlzmNxY[/youtube]
Getting the Code
You can download the completed project on Bitbucket.
Follow Us
Like our blog? Want to know when a new post is up? Follow us on Twitter at @iachievedit!