Mathew Sanders /

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.

Topics

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.

Straight from Apple’s documentation here are the properties that can be animated on any UIView object.

  • 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.
let duration = 1.0 
let delay = 0.0 // delay will be 0.0 seconds (e.g. nothing) 
let options = UIViewAnimationOptions.CurveEaseInOut // change the timing curve to `ease-in ease-out`

UIView.animateWithDuration(duration, delay: delay, options: options, 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
            
})

4. Animation block with spring physics

A new addition in iOS 7, this method allows us to create a physics-engine based animation.

  • duration: number of seconds that the animation will take;
  • delay: how long to wait before animation starts;
  • dampingRatio: how much oscillation (0= infinite 1=none);
  • initialVelocity: how fast the animation starts (1 is ‘normal’);
  • 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.
let duration = 1.0 
let delay = 0.0 
let options = UIViewAnimationOptions.CurveEaseInOut 
let damping = 0.5 // set damping ration
let velocity = 1.0 // set initial velocity 

UIView.animateWithDuration(duration, delay: delay, usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: options, 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
                
})

UIViewAnimationOptions

Apple have given us a bunch of options that we can pass into the animation block methods.

Lets see what happens when we pass in an option of ‘Repeat’.


let options = UIViewAnimationOptions.Repeat
        
UIView.animateWithDuration(1.0, delay: 0.0, options: options, animations: {

    // any changes entered in this block will be animated
    coloredSquare.backgroundColor = UIColor.redColor()
    coloredSquare.frame = CGRect(x: 320-50, y: 120, width: 50, height: 50)

}, completion: nil) 

// Note: I didn't want to do anything with the completion block 
// in this example so I set it to 'nil'

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.

We’ll combine the animation options Autoreverse, Repeat and the timing-curve option CurveEaseInOut.

let options = UIViewAnimationOptions.Autoreverse | UIViewAnimationOptions.Repeat | UIViewAnimationOptions.CurveEaseInOut

Great, you’re starting to get the hang of block-based animations! You can review all of UIViewAnimationOptions on Apple’s developer website.

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.

UIView.animateWithDuration(1.0, animations: {

    // animate color change and position 
    coloredSquare.backgroundColor = UIColor.redColor()
    coloredSquare.frame = CGRect(x: 320-50, y: 120, width: 50, height: 50)
            
}, completion: { animationFinished in
    
    // when complete, remove the square from the parent view
    coloredSquare.removeFromSuperview()
            
})

Now when the animation completes, the square should be removed setting you up for your next animation.

Generative animations

Now that we have our animation set up to act on a trigger we can really start exploring the benefits of creating animations programmatically: generative animations!

First off we need to do some housekeeping to prepare our code to be more reusable. In programming this is called refactoring.

Lets change the code that constructs the square from using hard-coded variables for the position and size of the square…

let coloredSquare = UIView()
coloredSquare.backgroundColor = UIColor.blueColor()
coloredSquare.frame = CGRect(x: 0, y: 120, width: 50, height: 50)
self.view.addSubview(coloredSquare)

…and switch to using named constants for size and yPosition.

// 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.

// set up some constants for the animation
let duration = 1.0
let delay = 0.0
let options = UIViewAnimationOptions.CurveLinear

// define the animation
UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
    
    coloredSquare.backgroundColor = UIColor.redColor()
    
    // again use the square constants size and yPosition
    coloredSquare.frame = CGRectMake(320-size, yPosition, size, size)
    
}, completion: { animationFinished in
    
    coloredSquare.removeFromSuperview()

})

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.

Our entire IBAction function should now look something like this:

@IBAction func animateButtonPressed(sender: AnyObject) {
    
    // loop for 10 times
    for loopNumber in 0...10 {
    
        // set up some constants for the animation
        let duration : NSTimeInterval = 1.0
        let delay : NSTimeInterval = 0.0 
        let options = UIViewAnimationOptions.CurveLinear
        
        // set up some constants for the fish
        let size : CGFloat = CGFloat( arc4random_uniform(40))+20
        let yPosition : CGFloat = CGFloat( arc4random_uniform(200))+20
        
        // create the fish and add it to the screen
        let fish = UIImageView()
        fish.image = UIImage(named: "blue-fish.png")
        fish.frame = CGRectMake(0, yPosition, size, size)
        self.view.addSubview(fish)
    
        // define the animation
        UIView.animateWithDuration(duration, delay: delay, options: options, animations: {

            // move the fish
            fish.frame = CGRectMake(320-size, yPosition, size, size)
         
        }, completion: { animationFinished in

            // remove the fish
            fish.removeFromSuperview()

        })
    }
}

Now when we tap the animate button we have 10 fish generated with each one a random size, and at a random y-position.

But now they’re all clumped together ಠ_ಠ. What we really want is for them to be staggered a little bit, again by some random amount.

There are a couple of ways to do that: we could push the x-position back by some amount; or we could randomly apply a delay to the animation.

Lets use the delay approach, just to show we can dynamically change the parameters of the animation to change it’s behavior.

We’ll change the delay constant of the animation from 0.0

let delay = 0.0 

… to be a random value between 0.9 and 1.0

// randomly assign a delay of 0.9 to 1s
let delay = NSTimeInterval(900 + arc4random_uniform(100)) / 1000

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).

let numberOfFish = Int(self.numberOfFishSlider.value)
for loopNumber in 1...numberOfFish {
    // loop contents
}

Now when we run the app we can dynamically change the number of fish in the animation by moving the value of the slider.

You can create as many sliders (or other interface objects) as you like to control the parameters of the animation.

Ideally you’d also want to include a label for each slider to note what you’re changing and to show what the current value is. but I’ll leave that as an exercise for you to try!

We’ve done quite a lot in one step here so if you’re having any errors running the app, here’s what the complete code for your ViewController.swift should look like:

import UIKit

class ViewController: UIViewController {
    
    // this time we've added an IBOutlet for the slider
    // this allows us to reference the slider from code
    // for example get the current position of the slider
    @IBOutlet weak var numberOfFishSlider: UISlider!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // we don't have to do anything here at the moment
    }
    
    @IBAction func animateButtonPressed(sender: AnyObject) {
        
        // the slider value returns a float (e.g. 10.4)
        // to work in the loop we need to round down as an Int (e.g. 10)
        let numberOfFish = Int(self.numberOfFishSlider.value)
        
        for loopNumber in 1...numberOfFish {
        
            // set up some constants for the animation
            let duration = 1.0
            let options = UIViewAnimationOptions.CurveLinear
            
            // randomly assign a delay of 0.9 to 1s
            let delay = NSTimeInterval(900 + arc4random_uniform(100)) / 1000
            
            // set up some constants for the fish
            let size : CGFloat = CGFloat( arc4random_uniform(40))+20
        let yPosition : CGFloat = CGFloat( arc4random_uniform(200))+20
            
            // create the fish
            let fish = UIImageView()
            fish.image = UIImage(named: "blue-fish")
            fish.frame = CGRectMake(0-size, yPosition, size, size)
            self.view.addSubview(fish)
            
            // define the animation
            UIView.animateWithDuration(duration, delay: delay, options: options, animations: {
                
                // move the fish
                fish.frame = CGRectMake(320, yPosition, size, size)
                
            }, completion: { animationFinished in
                
                // remove the fish
                fish.removeFromSuperview()
            })
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

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.

If you liked this tutorial, Animations in Swift (Part Two) looks at some more complex animations like simple transitions and animating using curves.

Or Getting started with UIKit Dynamics in Swift by Omar Fouad has a similar style post looking at how UIDynamics APIs introduced in iOS 7 can be used to create interactive physics-based animations.

Read More Swift Animation Posts...
Animations
Part 2
Transitions
Part 1
Transitions
Part 2
Transitions
Part 3

Notes

  1. 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.

  2. 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.

  3. 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).

  4. 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.

  5. 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.

  6. 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.

  7. 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.