Prototyping Animations in Swift

With every release, Apple have been making developer tools easier and more productive to use.

Now with Xcode 6 and iOS 8, Apple have created a new programming language called Swift that is not only more powerful, but also has a syntax that designers will probably feel a lot more comfortable with compared to Objective-C.

If you’ve ever wanted to learn enough programing to create rich prototypes and interactive or generative animations now’s a great time to begin.

This tutorial will show you the basics of creating animations with Swift1.

Part Two looks at some more complex animations like simple transitions and animating using bezier curves.

Why Xcode?

Motion software like After Effects are extremely powerful and an important part of any designers toolkit.

But with any motion software the best you can hope for is an simulation of the actual experience. Prototyping with developer tools like Xcode aren’t simulations, they are mini apps that will bring you as close to the final experience as you can possibly get. Along with testing on device, you can take effect of native abilities and do fun things like use the the device gyroscope to programmatically alter your animations.

There’s no doubt that Xcode comes with a steel learning curve, but the same could be said for tools you’ve already put the effort into learning (Photoshop for me is still notoriously complex) and learning Xcode will be a skill that will continue to pay off for years to come in your career.

Finally, when developer timelines start to crunch, it’s often magic like animations that start to get cut from a product. The easier you make an animation to build, then the more likely it will be included. And developers will likely find working from a prototype a lot easier than inspecting an After Effects file, or trying to comprehend a specification.

Let’s start!

To begin, create a new project with the Single View Application template2. Make sure you choose Swift as the language and not Objective-C.

Open the ViewController.swift file and add add a blue square to the screen by adding this cope to your viewDidLoad() method.

// Create and add a colored square
let coloredSquare = UIView()
// set background color to blue
coloredSquare.backgroundColor = UIColor.blueColor()
// set frame (position and size) of the square
// iOS coordinate system starts at the top left of the screen
// so this square will be at top left of screen, 50x50pt
// CG in CGRect stands for Core Graphics
coloredSquare.frame = CGRect(x: 0, y: 120, width: 50, height: 50)
// finally, add the square to the screen
self.view.addSubview(coloredSquare)

Run the project and you should see something like this in the iPhone simulator:

Next, we’re going to add a simple animation that moves the square from the left edge of the screen to the right.

We do this with the method animateWithDuration that Apple gives us as part of UIView class.

The animateWithDuration function takes two parameters, the duration of the animation, and a block3 that defines the end-state of the animation.

// lets set the duration to 1.0 seconds
// and in the animations block change the background color
// to red and the x-position of the frame
UIView.animateWithDuration(1.0, animations: {
coloredSquare.backgroundColor = UIColor.redColor()
// for the x-position I entered 320-50 (width of screen - width of the square)
// if you want, you could just enter 270
// but I prefer to enter the math as a reminder of what's happenings
coloredSquare.frame = CGRect(x: 320-50, y: 120, width: 50, height: 50)
})

Run the project again and when the simulator launches you should see the square move from the left edge to the right edge, and color change from blue to red:

Notice that iOS automatically does the tweening of the animation. We just define the beginning state, and the end state, and the time it should take to transition between the two and iOS creates a smooth animation.

What can be animated?

We’ve animated the position (via the frame) and background color properties of our UIView but there are lots more we could have animated.

frame: Change the view’s size and position relative to its superview’s coordinate system.

bounds: Change the view’s size.

center: Change the view’s position.

transform: Modify this property to scale, rotate, or translate the view relative to its center point in 2D space4.

alpha: Change the transparency of the view.

backgroundColor: Change the view’s background color.

contentStretch: Change the way the view’s contents are stretched to fill the available space.

Block animation options

Apply currently gives us four block-based animation methods to use. We’ve just used the simplest one. Here’s a list of all four:

1. Simple animation block

duration: number of seconds that the animation will take; and

animations: a block defining changes to animate.

let duration = 1.0 // animation will take 1.0 seconds
UIView.animateWithDuration(duration, {
// any changes entered in this block will be animated
})

2. Animation block with completion block

duration: number of seconds that the animation will take;

animations: a block defining changes to animate; and

completion: another block this one defining code to run when the animation has completed5.

let duration = 1.0 // animation will take 1.0 seconds
UIView.animateWithDuration(duration, animations: {
// any changes entered in this block will be animated
}, completion: { finished in
// any code entered here will be applied
// once the animation has completed
})

3. Animation block with options & completion block

duration: number of seconds that the animation will take;

delay: how long to wait before animation starts;

options: various options we can use to change animation behavior;

animations: a block defining changes to animate; and

completion: another block this one defining code to run when the animation has completed.

The square performs the animation, then repeats the animation again until the app is closed.

Now let’s change that to Autoreverse

let options = UIViewAnimationOptions.Autoreverse

The square performs the requested animation, then the animations are performed in exact reverse order. But There’s a bit of a weird glitch where when the animation completes the square automatically jumps to the left edge of the screen and switches back to red. That’s because this is the last condition we left it.

Finally, lets see how we can combine multiple options by joining them with the pipe character.

Triggering animations

It gets a little annoying having to re-run the app each time we want to see the animation - especially when the animation isn’t on loop.

Lets update the app so that the animation doesn’t run by default, but instead only happens when a button is manually tapped.

First. Switch from files ViewController.swift to Main.storyboard, drag a Button object onto the storyboard and change the button label to “Animate”.

Then add constraints to the button6. I added constraints to force a gap of 20pt on the bottom edge and both side edges.

Next, switch the assistant editor on (which allows you to see the storyboard and swift class file at the same time).

Holding the control key down, drag from the button into a space right below the viewDidLoad() function in the ViewController.swift file and release.

This will open a popover allowing you to make a connection. By default the connection type will be Outlet. Change this to Action and in the name textfield enter a name for your function and press the Connect Button.

Xcode will add a new function to your ViewController that looks something like this:

@IBAction func animateButtonPressed(sender: AnyObject) {
}

This function is connected to the button in your storyboard, and it will be called every time the button is tapped. This is how you create hooks between actions performed in the interface to your code7.

Finally, cut and paste the code from your viewDidLoad() function into your new IBAction function so that the animation will only perform when the Animate button is tapped.

Your IBAction function should now look something like this:

@IBAction func animateButtonPressed(sender: AnyObject) {
// Create and add a colored square
let coloredSquare = UIView()
// set background color to blue
coloredSquare.backgroundColor = UIColor.blueColor()
// set frame (position and size) of the square
// iOS coordinate system starts at the top left of the screen
// CGRect creates a frame with (x,y,width,height) values
// so this square will be at top left of screen, 50x50pt
// CG in CGRectMake stands for Core Graphics
coloredSquare.frame = CGRect(x:0, y:120, width:50, height:50)
// finally, add the square to the screen
self.view.addSubview(coloredSquare)
// lets set the duration to 1.0 seconds
// and in the animations block change the background color
// to red and the x-position of the frame
UIView.animateWithDuration(1.0, animations: {
coloredSquare.backgroundColor = UIColor.redColor()
// for the x-position I entered 320-50 (width of screen - width of the square)
// if you want, you could just enter 270
// but I prefer to enter the math as a reminder of what's happenings
coloredSquare.frame = CGRect(x: 320-50, y: 120, width: 50, height: 50)
})
}

Run the project again and trigger the animation by tapping the Animate button.

Unfortunately, once the animation ends, the square remains in it’s final position. This is where an animation function that uses a completion block is useful.

So now, we’ll update your animation block to include a completion block. For now all we’ll do in the completion block is remove the square.

// set up some constants for the square
let size : CGFloat = 50
let yPosition : CGFloat = 120
// create the square using these constants
// in this example I've also used the Objective-C convention for making the CGRect
// but I could have used CGRect(x:0, y:yPosition, width:size, height:size) like we've done previously - they are equivalent
let coloredSquare = UIView()
coloredSquare.backgroundColor = UIColor.blueColor()
coloredSquare.frame = CGRectMake(0, yPosition, size, size)
self.view.addSubview(coloredSquare)

And lets refactor the our animation block so that it also uses the same square constants to define the animation. While we’re at it, lets create constants duration, delay and options for the animation too.

We’ve not changed any behavior of the code, we’ve just structured it in a different way that makes it easier for the next step.

So running the app again shouldn’t have any noticeable effect.

So. Now lets change the constants we created for the square so that instead of being a fixed number, they are assigned from a range of random numbers.

We’ll use the arc4random_uniform function which returns a whole number between zero and the number you pass into the function. For example arc4random_uniform(4) will randomly return either 0, 1, 2, 3, or 4 each time it’s called.

We can use this and some simple math to make random numbers in a useful range for the animations.

// set size to be a random number between 20.0 and 60.0
let size : CGFloat = CGFloat( arc4random_uniform(40))+20
// set yPosition to be a random number between 20.0 and 220.0
let yPosition : CGFloat = CGFloat( arc4random_uniform(200))+20

Now each time you tap the animate button, the square will be a different size, and start from a different y-position.

Now we have a reusable randomly-generated square and animation, we can use it multiple times by putting it into a loop.

Loops in Swift are a lot easier than Objective-C. Here’s the basic code for a loop that goes from 0 to 10.

for loopNumber in 0...10 {
// code in here will be performed in each loop
// `loopNumber` will keep track of which loop we're currently in
}

Lets all of our animation code so that it sits within the scope of the loop.

Until now our animations have been pretty abstract. Now we’ll pretend that our aim this entire time has been to create an animation of a school of fish swimming across the screen. So we’ll also remove the code that animates the background color, and change our UIView object coloredSquare to the UIImageView object we’ll call fish.

At the moment the fish appear instantly at the left edge of the screen, and they are only removed after the animation finishes when they touch the right edge of the screen.

Lets change that so that the fish starts it’s animation from just off the left of the screen, and completes it when it finally goes over the right edge of the screen.

First we’ll move the starting position from 0…

fish.frame = CGRectMake(0, yPosition, size, size)

… to 0-size so that the animation starts outside the edge of the screen.

fish.frame = CGRectMake(0-size, yPosition, size, size)

And in the animation block, instead of moving the fish to 320-size which will move the fist until it touches the right edge of the screen…

fish.frame = CGRectMake(320-size, yPosition, size, size)

… we’ll move it all the way to 320 which is the width of the screen (remember that we define the position of an object from the top and left position).

fish.frame = CGRectMake(320, yPosition, size, size)

Now when we tap the animate button, 10 fish are created, each positioned to start just off the edge of the screen, and with each animation set to start with a slightly different delay.

This gives a closer effect to what we were hoping for:

This is a very simple example, but hopefully by now you’re getting an idea of what’s possible with generating animations programmatically.

Dynamically exploring constants

We’ve now got a lot more control over the animation now, but to perfect the animation we’re going to want to explore fine tuning a lot of different details.

For example, what does the animation look like with 20 fish, or 100 fish instead of 10? What about exploring different sizes of fish or different animation duration or delays?

Having to run the app every time you want to do this is not only annoying, but also makes it really difficult to quickly explore options with other team members.

Similar to how we added a Button to the storyboard to trigger the animation, now we’re going to add a Slider to the storyboard that controls the number of fish in the animation.

Add the slider to the storyboard (right above the button is a good place). And add some sensible constraints for the position of the slider.

By default the slider has a min value of 0 and a max value of 1. Show the attributes inspector (the default key binding is option-command-4) and change the min value to 1 and the max value to 100.

Then control-drag onto the ViewController.swift file, but this time let go right above the viewDidLoad() function. This time keep the connection type as Outlet and enter a name like numberOfFishSlider.

Xcode should create an IBOutlet for you with the name you chose.

@IBOutlet weak var numberOfFishSlider: UISlider!

Now, the only change we need to make is to change the loop from going from 0...10 to start from 1 and loop up to the current value of our slider.

We can get the current position of the slider with self.numberOfFishSlider.value but this will be a number with decimal places (e.g. 12.4) so we’ll cast the number to an Int to get the whole number (e.g. 12).

Fin

Depending on your experience with code this might be either amazing easy or frustratingly difficult.

My only advice is that like most things, it becomes easier the more you practice, and the more you attempt you own real-life examples (rather than step-by-step tutorials like this one).

If you’re getting stuck and need some help I’m willing to help! Hit me up on twitter @permakittens. If you’re having problem with a specific bit of code it helps to upload your project somewhere like github where I can more easily review.

Notes

To start using Swift, you’ll need to download a copy of Xcode 6. As of today Xcode 6 is only available as a beta release to registered Apple Developers (it’s free to sign up). Once iOS 8 launches it will also be available to download from the iTunes app store. ↩

If you’re not familiar with Xcode there are a bunch of great tutorials to help you get started. http://codewithchris.com/first-xcode-project/ is directed at Xcode 5, but the differences in starting a new project are pretty minimal. ↩

An easy way to think about blocks is to think of them as a block of code that we pass into a function. The function will then use that code, often by calling it at a particular time (e.g. when something is about to start, or when something has just finished). ↩

Animating in 3D space is possible too, but instead of animating the UIView Object, you’ll need to animate the views CALayer (CA stands for Core Animation). This also gives a few more properties that can be animated such as shadow and border properties. ↩

Why would we want to run code when an animation has completed? First we may want to clean up the screen (e.g. remove objects that were added as part of an animation). Second, by adding another block-based animation in the completion block you can create complex multi-step animations by chain animations together to form a sequence of animations. ↩

Constraints are an responsive layout tool that describe how an object should be positioned on the screen. For example by defining that a button should have 20pt gap on its left and right edges means that the button will correctly grow (or shrink) on different device sizes. This allows us to create a layout that works on iPhone, iPad and at different orientations. ↩

The IBOutlet on the other hand creates a hook from your code back to the storyboard allowing you to pragmatically alter the objects that are connected through an IBOutlet (for example allowing code to change the text of a button). The ‘IB’ in IBAction stands for Interface Builder which was it’s own app that Apple created to help developers build interfaces. It’s since been integrated into Xcode as storyboard but the IB name stuck around. ↩