Animations in Swift (pt 2)

In Prototyping iOS Animations in Swift I introduced the a range of block-based functions that UIKit provides to create tweened animations and how a simple animation can be programmatically altered with some random variation to create more complex scenes.

This alone can create an interesting range of animations, but is still only the tip of the iceberg of what Apple provides as tools for creating animations.

This tutorial looks at some more animation functions that require a little bit more setting up to create, but once mastered opens up an even larger number of possibilities.

Topics

Container view transitions

If you want to perform an animated transition between two views, Apple provides a handful of default animations that are easy to create with just a small bit of code.

The trick to using these methods is that the transition needs to be performed in a parent container, which is typically just an invisible UIView that’s the size of the largest object you’re transitioning with. So performing this animation requires a little bit of setting up.

For example, to animate a transition between two colored UIViews we’ll use a third UIView as the container for the animation.

Switching from one view to another is such a common task that so long as we don’t have anything else we also want to animate with the transition, Apple provides a slightly easier function that does the removeFromSuperview() and addSubView() for you automatically.

Now iOS has enough information to create the animation as we expected.

If you’re manually entering values for relativeStartTime & relativeDuration it’s easy to make a mistake, but if all you want to achieve is a smooth transition between each of the keyframes, you can enter CalculationModePaced as an option which ignores any values you’ve entered for relativeStartTime and relativeDuration and automatically figures out correct values for a consistent animation:

Moving an object along a bezier curve

A really fun animation to create is move the position of an object along a curve.

Our basic animation techniques make it easy to animate an object moving from point A to B, but to have an object move along the multiple points of a curve A,B,C,D,E we’ll need to use a keyframe-based animation again.

You could do this manually using the keyframe block function like we did for the rotation animation, but to get a nice smooth animation we’d have to define a lot of keyframes and it would get quickly get very complicated and messy.

Luckily, instead of assigning each keyframe manually, we can give iOS a bezier curve3 and the keyframes needed will be generated automatically.

This requires us to use more powerful animation features of iOS that are slightly more complicated, but not too hard once you get the general approach.

// first set up an object to animate
// we'll use a familiar red square
let square = UIView()
square.frame = CGRect(x: 55, y: 300, width: 20, height: 20)
square.backgroundColor = UIColor.redColor()
// add the square to the screen
self.view.addSubview(square)
// now create a bezier path that defines our curve
// the animation function needs the curve defined as a CGPath
// but these are more difficult to work with, so instead
// we'll create a UIBezierPath, and then create a
// CGPath from the bezier when we need it
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: 16,y: 239))
path.addCurveToPoint(CGPoint(x: 301, y: 239), controlPoint1: CGPoint(x: 136, y: 373), controlPoint2: CGPoint(x: 178, y: 110))
// create a new CAKeyframeAnimation that animates the objects position
let anim = CAKeyframeAnimation(keyPath: "position")
// set the animations path to our bezier curve
anim.path = path.CGPath
// set some more parameters for the animation
// this rotation mode means that our object will rotate so that it's parallel to whatever point it is currently on the curve
anim.rotationMode = kCAAnimationRotateAuto
anim.repeatCount = Float.infinity
anim.duration = 5.0
// we add the animation to the squares 'layer' property
square.layer.addAnimation(anim, forKey: "animate position along path")

Now we have a single animation, lets use it multiple times to create a more complex scene.

Now we have multiple squares animating, but they all start at the same time so don’t look so great.

Lets set some more properties to the animation object that adds some randomness to how long the animation takes (so that some of the squares will move faster than others), and at what position of the animation it starts (so that they appear staggered).

// each square will take between 4.0 and 8.0 seconds
// to complete one animation loop
anim.duration = Double(arc4random_uniform(40)+30) / 10
// stagger each animation by a random value
// `290` was chosen simply by experimentation
anim.timeOffset = Double(arc4random_uniform(290))

Now our squares follow a bezier curve with with much more variation due to the randomness we’ve added to each animation.

From here, it’s not too much of a step to switch the redSquares to images and add a background to create an interesting organic animation like a school of fish or a flock of birds4.

Animating appearance of a bezier curve

Another useful technique to know that also uses an curve it to animate how much of the curve is drawn.

When we animated an object along a curve, the bezier path wasn’t actually shown on the screen, instead it was used as an input to the keyframe animation.

In this example, we’ll actually draw the curve to the screen, but animate how much of the curve is shown from 0 to 100%.

Lets add this code into an @IBAction function that’s triggered when a button is tapped.

This animation uses a very simple oval as a curve, but this technique can be applied to any curve at all. I’ve seen examples where someone has converted cursive text to create the illusion of a word being written, or you could combine it with animating an object along the same curve to show the path it’s taken while animating.

System default animation(s)

Another addition with iOS 7 is UIView.performSystemAnimation which for now only has UISystemAnimation.Delete as a valid option but I’m hoping that in the future Apple will add more standard system animations that can be easily created using this function.

Notes

Tuples are one of the reasons I love another programming language called Haskell. Most programming languages are designed so that a variable contains a single object. Tuples allow us to create an ad-hoc data structure that contains two (or more) objects of our choice. ↩

Splitting the animation into two halves is still potentially ambiguous since the rotation (in degrees) from 0 to 180 could equally be achieved either by moving clockwise or anti clockwise. It might work to define the rotation as (0 to 180 then 180 to 360) but since 0 and 360 are equivalent you might end up with the rotation backtracking on itself. ↩

Making bezier curves from control points isn’t easy to do if you’ve never worked with them outside of vector software before :(

Or, if you’re like me and that’s just too much, download PaintCode which (among other useful things) will automatically create Swift code for a bezier curve you’ve drawn on screen. ↩

For this animation I also added a random size for each fish, but instead of randomly adding variation to the speed of each fish I calculated the duration of each animation based off the size so that smaller fish take longer to complete one loop than the larger fish. This helps create an illusion of depth and perspective since closer objects should appear to move faster. ↩

For the record, I’m not a French New Wave fan: sadly it was a reference to Sharknado. ↩