Chapters

Hide chapters

iOS Animations by Tutorials

Sixth Edition · iOS 13 · Swift 5.1 · Xcode 11

Section IV: Layer Animations

Section 4: 9 chapters
Show chapters Hide chapters

7. Keyframe Animations
Written by Marin Todorov

Keyframe animations are the a special sort of view animation that, instead of going from point A to point B, let you create animations with multiple milestones. Keyframes are another building block of complex, eye pleasing animations which allow you to upgrade from simple one-shot animations to designing complex animations sequences.

Let’s see how it would look like if you wanted to chain multiple simple animations together and move a view in a rectangular pattern:

To achieve this you could chain several animations and completion closures together like so:

UIView.animate(withDuration: 0.5, 
  animations: {
    view.center.x += 200.0
  }, 
  completion: { _ in
    UIView.animate(withDuration: 0.5, 
      animations: {
        view.center.y += 100.0
      }, 
      completion: { _ in
        UIView.animate(withDuration: 0.5, 
          animations: {
            view.center.x -= 200.0
          }, 
          completion: { _ in
            UIView.animate(withDuration: 0.5, 
              animations: {
                view.center.y -= 100.0
              }
            )
          }
        )
      }
    )
  }
)

Look at all that nesting! And that’s for a simple rectangle-shaped path; can you imagine what that would look like for a more complicated movement?

There must be a better way...
There must be a better way...

Instead, you can split the total animation into four distinct stages, or keyframes, and then combine the individual keyframes into a keyframe animation.

Keyframe animations

Consider another animation that depicts the take-off sequence of a plane. The distinct stages of this animation would look like the following:

The first stage of the animation sequence accelerates the plane on the runway. The second stage gives the plane a little altitude and tilts it upward. The third stage continues to tilt the plane and boosts the plane skyward at a much faster rate.

There’s a fourth and final stage in the final 10% of the animation that fades the plane out of view, as if it were moving behind some low-hanging clouds.

The complete animation could be overwhelming to create, but breaking down the animation into the various stages makes it a lot more manageable. Once you’ve defined the keyframes for each stage, you’ve got the problem mostly solved.

You’ll learn about keyframes and how to assemble them into a keyframe animation by creating an airplane animation similar to the above — but it will be far more complex. Don’t worry, you’re more than capable of handling this!

Setting up your keyframe animation

If you worked through the project in the last chapter, you can use that as your starting point; if you didn’t do that, or if you want to start fresh, you can use the starter project for this chapter.

Build and run your project; you should see the two alternating connecting flights:

You’ll make the airplane take off from its starting position, circle around, then land and taxi back to the starting point. This animation will run each time the screen switches between connecting flights.

The complete animation will look something like this:

Open ViewController.swift; all the code for this animation will live in a new class method named planeDepart().

Find changeFlight(to:,animated:) and you’ll see an if animated statement near the top containing all of the animated changes. Add the following line at the top of the if block (be careful to place it inside the block):

planeDepart()

Now add the following initial version of your new animation method planeDepart() as shown below:

func planeDepart() {
  let originalCenter = planeImage.center

  UIView.animateKeyframes(withDuration: 1.5, delay: 0.0,
    animations: {
      //add keyframes
    }, 
    completion: nil
  )
}

You store the original position of the airplane in originalCenter so you’ll know where your animation should finish.

You also use animateKeyframes(withDuration:delay:options: animations:completion:) to create your first keyframe animation. The parameter list is identical to the ones you’ve used all along in this book to create your view animations. You set the duration to 1.5 seconds, add no other options and add an empty animations closure.

The options for keyframes are different; they come from the UIViewKeyFrameAnimationOptions enumeration instead of UIViewAnimationOptions. You’ll learn about the fine details of keyframe options a little later in this chapter.

Adding keyframes

It’s time to add your first keyframe. Replace the //add keyframes comment with the following code:

UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.25,
  animations: {
    self.planeImage.center.x += 80.0
    self.planeImage.center.y -= 10.0
  }
)

This is the first of several calls to addKeyframe(withRelativeStartTime: relativeDuration:animations:) you’ll be adding.

The animation keyframe above is little different than the animation methods you’ve used so far.

addKeyframe(withRelativeStartTime:relativeDuration:animations:) is the first time you’ve used relative durations in the animations in this book. The start time of the keyframe, as well as its duration, are expressed as percentages relative to the entire duration of the animation. For example, 0.1 would be 10%, 0.25 would be 25%, and 1.0 would be 100% of the total duration.

Working with relative values lets you specify that a keyframe should last for a fraction of the total time; UIKit takes the relative durations of each keyframe and automagically figures out the exact durations for each keyframe, saving you a ton of work.

In the final bit of code above you set the start time to 0.0 — meaning “immediately” — and the duration to 0.25, representing 25% of the total animation time:

Since you set the duration of the complete animation to 1.5 seconds, the first keyframe will last for approximately 0.375 seconds. Later on, if you decide to modify the total duration of the animation to a different value, the individual keyframe animations will recalculate their duration times accordingly.

Build and run your project and check out the first frame of your animation:

Running the first keyframe sets in motion the code in the animations closure: the airplane moves 80 points to the right and 10 points up.

This is, of course, only the first step in creating the complete animation sequence. To create the second keyframe, add the following code to the keyframe block, underneath the previous addKeyframe call:

UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 0.4) {
  self.planeImage.transform = CGAffineTransform(rotationAngle: -.pi / 8)
}

The second keyframe starts 10% of the way into the animation and lasts for 40% of the total duration. This keyframe rotates the airplane as it moves off the runway:

Build and run your project now and see how the two keyframes look when they run together:

Keep building your sequence by adding the third keyframe as follows:

UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25) {
  self.planeImage.center.x += 100.0
  self.planeImage.center.y -= 50.0
  self.planeImage.alpha = 0.0
}

In this stage of the animation, you keep the airplane moving along but start to fade it out.

Build and run your project and see how the three keyframes in your animation look:

The plane flies out of sight and disappears. Now you’ll need to bring it back and land it safely on the next connecting flight screen.

Before you show the airplane on the screen, you’ll have to reset the orientation of the plane — that is, undo the rotation you’ve applied — and move it to the left of the visible area.

Since the airplane is invisible with its alpha set to 0 for the moment, you can move it around without the user seeing anything. Add the following keyframe to your animation:

UIView.addKeyframe(withRelativeStartTime: 0.51, relativeDuration: 0.01) {
  self.planeImage.transform = .identity
  self.planeImage.center = CGPoint(x: 0.0, y: originalCenter.y)
}

This keyframe runs after all three previous frames have completed and lasts just a small fraction of a second; it resets the plane transform and moves it to the left edge of the screen.

Now you can add the final keyframe and taxi the airplane frame and taxi back to the original location:

UIView.addKeyframe(withRelativeStartTime: 0.55, relativeDuration: 0.45) {
  self.planeImage.alpha = 1.0
  self.planeImage.center = originalCenter
}

This keyframe fades the airplane back in while moving it to the original starting point of the animation.

Build and run your project to see the completed animation sequence as shown below:

Take a minute to review the code you wrote. It’s easy to follow and decode; you can modify, rearrange or alter the timing of any of the sections above without too much work on your part.

It’s a short step to imagine your animation as a series of separate animations with delays in between, or even as a set of animations triggered from completion closures. Keyframes are an incredibly useful and flexible way to design and control your animations.

Calculation modes in keyframe animations

Keyframe animations don’t support the built-in easing curves that are available in standard view animations. This is by design; keyframes are supposed to start and end at specific times and flow into each other.

If each stage of your animation above had an easing curve, the plane would jerk around instead of moving smoothly from one animation into the next. If you could apply easing to the entire animation, that would result in your animation durations being ignored — and that’s not what you want.

Instead, you have several calculation modes to choose from; each mode offers a different method to calculate intermediate frames of the animation as well as different optimizers for smooth movement and even pacing. Check out the documentation by searching for UIViewKeyframeAnimationOptions for more details.

Now that you know how to group together any number of simple animations using keyframe animations, you can build just about any sequence that comes to mind. If you want to test your knowledge of keyframes and keyframe animations, give the challenge below a try before moving on to the next section.

Key points

  • You can “chain” animations by using keyframes much easier than using the completion closure on the standard APIs to produce sequences.
  • Keyframe animations not only allow you to “chain” animations but also to “overlap” them, and group them in general in any way you wish.
  • Don’t forget all keyframe timing values are relative to the complete animation, whereas the API defining the complete sequence uses absolute time.

Challenges

Challenge: Animate the flight departure times

There’s still one UI element on screen that isn’t animated: the black flight summary status bar at the top of the screen that shows the flight departure times.

Your challenge is to add a keyframe animation to move the summary offscreen each time the connecting flight data changes. Then you’ll change the text to reflect the departure time of the next flight animate it back on screen like so:

The departure summary label is already connected to an outlet called summary.

If you need a general structure for your solution, here’s a basic recipe you can follow:

  • Create a new method summarySwitch(to: String) to animate the summary label.

  • Inside the method, define a keyframe animation with two keyframes — one to animate the text out of the screen and another to animate it back in.

  • Finally, make a call to delay(seconds:completion:) outside of the keyframe animation and change the text of the summary label in the closure parameter. You need to time the text change to happen exactly when the label is offscreen.

  • Call summarySwitch(to) from within the “animated” part of changeFlight(to:,animated:)

Don’t forget to move the existing non-animated call inside the else statement.

How did you do? If you succeeded, congratulations! You’ve managed to animate every single UI element in the project.

The next section of this book delves into the seemingly complex world of Auto Layout and animations; you’ll learn that Auto Layout isn’t nearly as mystifying as you might have thought, and you’ll learn to animate constraints just as easily as you animate views!

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.
© 2024 Kodeco Inc.