Chapters

Hide chapters

iOS Animations by Tutorials

Seventh Edition · iOS 15 · Swift 5.5 · Xcode 13

Section IV: Layer Animations

Section 4: 9 chapters
Show chapters Hide chapters

14. Layer Keyframe Animations & Struct Properties
Written by Marin Todorov

Keyframe animations on layers are a bit different than keyframe animations on a UIView. View keyframe animations are a simple way to combine independent simple animations together; they can animate different views and properties, and the animations can overlap or have gaps in between.

In contrast, CAKeyframeAnimation lets you animate a single property on a given layer. You can define different key points of the animation, but you can’t have any gaps or overlaps in your animation. Even though that sounds restrictive at first, you can create some very compelling effects with CAKeyframeAnimation.

In this chapter, you’ll create a number of layer keyframe animations, from the very basic to more advanced animations that simulate real-world collisions. In Chapter 17, “Stroke & Path Animations”, you’ll learn how to take layer animations even further and animate your layers along a given path.

For now, you’ll walk before you run and create a funky wobbly effect for your first layer keyframe animation.

Introducing Keyframe Animations

Think for a moment how a basic animation works. Using fromValue and toValue, Core Animation progressively modifies a particular layer property between those values over a specified duration. For instance, when you rotate a layer between 45° and -45° (or π/4 and -π/4 for you math types out there) you only need to specify those two values and the layer renders all intermediate values to complete the animation:

Instead of fromValue and toValue, CAKeyframeAnimation uses an array of values to animate through, named values. The elements of values are the measured milestones of your animation. You’ll also need to supply the time that the animation should reach each value’s key point.

Take a look at the following simple layer keyframe animation example:

In the above animation, the layer rotates from 45° to -45°, but this time it has two separate stages: first, it rotates from 45° to 22° during the first two-thirds of the animation duration, and then it rotates all the way to -45° in the time remaining.

In essence, animating layers with keyframes requires you to provide key values for the property you’re animating, along with a corresponding number of number of relative key times that progress between 0.0 and 1.0.

Creating a Layer Keyframe Animation

Open the starter project for this chapter, or alternatively if you worked through the project from the previous chapter you can pick up where you left off.

Open ViewController.swift and find resetForm(); this method executes when you finish animating the different login status messages from “Connecting” to “Authorizing” and so on. You’ll animate the form title slightly on the faux failed authentication attempt to tell the user that that an error has occurred.

Add the following code to the end of resetForm():

let wobble = CAKeyframeAnimation(keyPath: "transform.rotation")
wobble.duration = 0.25
wobble.repeatCount = 4
wobble.values = [0.0, -.pi / 4.0, 0.0, .pi / 4.0, 0.0]
wobble.keyTimes = [0.0, 0.25, 0.5, 0.75, 1.0]
heading.layer.add(wobble, forKey: nil)

Here you create a new CAKeyframeAnimation in the same way you usually do for a CABasicAnimation: you specify a keyPath, set the animation’s total duration and indicate how many times you want it to repeat.

Then you set up your animation values in the values array: you rotate the layer from 0° to -45° (equal to π/4), back to 0°, all the way to 45° and finally back to 0°. The animation starts and ends on the same value, which makes repeating it an easy task.

Finally, you set the key times for the values of the animation, making sure to set the start and end times to 0.0 and 1.0 respectively to avoid any jumps in your animation.

Build and run your project; tap on the login button, wait until the end of the sequence and you’ll see the title wobble to alert the user that their login attempt failed:

Keen-eyed readers have probably noticed that I haven’t yet covered animations on struct properties.

Most of the time, you can get away with animating a single component of a struct, such as the x component of a CGPoint, or the rotation component of a CATransformation3D, but you’ll find out next that there’s more to animating struct values than you might think at first.

Animating Struct Values

Struct instances are first-class citizens in Swift. In fact, there’s very little difference syntactically between working with classes and structs.

However, Core Animation is an Objective-C framework built on C, which means that structs are handled very differently. Objective-C APIs like to deal with objects, so structs need some special handling.

This is why it’s relatively easy to animate a layer property such as a color or a number, but it’s not quite as easy to animate a struct property such as a CGPoint.

There are many animatable properties of CALayer that hold struct values, including position of type CGPoint, transform of type CATransform3D, and bounds of type CGRect. To help manage this, Cocoa includes the NSValue class, which “boxes in” or “wraps” a struct value as an object. NSValue comes with a number of convenience initializers you can use for each struct you need to box, including the following:

init(cgPoint: CGPoint)
init(cgSize: CGSize)
init(cgRect rect: CGRect)
init(caTransform3D: CATransform3D)

How would you use these initializers to box your values? Here’s what a sample position animation using CGPoint would look like:

let move = CABasicAnimation(keyPath: "position")
move.duration = 1.0
move.fromValue = NSValue(cgPoint: CGPoint(x: 100.0, y: 100.0))
move.toValue = NSValue(cgPoint: CGPoint(x: 200.0, y: 200.0))

If you try to assign a CGPoint directly to fromValue or toValue your animations will not work as you expect. Instead, you would box the CGPoint in an NSValue before assigning it to fromValue and toValue.

The same thing happens with keyframe animations: if you try to assign an array of CGPoint instances as the values of your animations the animations will not work since you have to use an array of boxed CGPoint NSValues instead.

The final section of this chapter will walk you through boxing your struct properties as you add the final layer keyframe animation involving, of all things, a hot-air balloon!

Intermediate Keyframe Animations

First you need to add the balloon image on screen. Open ViewController.swift, then add the following code to the bottom of login():

let balloon = CALayer()
balloon.contents = UIImage(named: "balloon")?.cgImage
balloon.frame = CGRect(x: -50.0, y: 0.0, width: 
50.0, height: 65.0)
view.layer.insertSublayer(balloon, below: username.layer)

In the code above, you create a new layer with the balloon image as its contents. If you need to show an image on screen but don’t need all the benefits of using a UIView (such as Auto Layout constraints, attaching gesture recognizers and so forth), you can simply use a CALayer like in the code example above. You position the layer near the top left corner, just outside of the visible area of the screen. Finally, you insert the layer below the username field so the balloon appears behind all the other elements in your form.

Now you can create the animation in few familiar steps. Add the following code underneath the previous code:

let flight = CAKeyframeAnimation(keyPath: "position")
flight.duration = 12.0

Here you create a keyframe animation and set its duration to 12.0, the approximate duration of the faux authentication process.

Next, add the following key value points and times:

flight.values = [
  CGPoint(x: -50.0, y: 0.0),
  CGPoint(x: view.frame.width + 50.0, y: 160.0),
  CGPoint(x: -50.0, y: loginButton.center.y)
].map { NSValue(cgPoint: $0) }

flight.keyTimes = [0.0, 0.5, 1.0]

Note how you use map to neatly convert an array of points into an array of points boxed as NSValues. Ain’t Swift great?

This animates the balloon along the path that connects the three points you assigned to values, like so:

Add the final few following lines to run the animation and to set the final position of the balloon layer:

balloon.add(flight, forKey: nil)
balloon.position = CGPoint(x: -50.0, y: loginButton.center.y)

Build and run your project; watch as the balloon flies across the screen as soon as you tap the Log In button:

You can use the same technique to animate other struct properties such as bounds, position, and transform.

If you want to animate the balloon over a more complex set of points, such as a smooth curved path, stay tuned for Chapter 17, “Stroke & Path Animations” where you’ll learn how to animate a layer over an arbitrary path using a special case of keyframe animation. You’ve covered all of the chapters in this book that deal with basic layer animations; the next chapters will introduce you to some of the cool animations you can create using specialized layers.

Key Points

  • You can easily create layer keyframe animations by using the CAKeyframeAnimation class.
  • Unlike views, layer keyframe animations animate a single property in a continuous animation over several possible key-points.
  • You can animate complex property data types by wrapping them as an NSValue type.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.