Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 51

`CAAnimationGroup`: Orchestrating Parallel Animations

Recommended resources

Hello! Welcome back to our final lesson in the "Core UIKit Animations" module.

In our last session, we dove into CABasicAnimation and learned how to animate a single CALayer property. You mastered the core pattern of creating an animation, applying it, and updating the model layer to make the final state permanent.

Today, we'll build directly on that knowledge. Our goal is to group multiple CAAnimation objects to run in parallel using CAAnimationGroup. Many rich visual effects require several properties to change in perfect sync—for example, an object fading out while scaling down and moving off-screen. CAAnimationGroup is the Core Animation tool that orchestrates these simultaneous animations.

Why Do We Need Animation Groups?

Imagine you want to create an effect where a circle pulsates, expanding outwards. As we animate its scale, we also need to animate its frame or bounds to ensure it expands from its center correctly, rather than just scaling from its top-left corner. These two animations—a scale transform and a bounds change—must happen at the same time.

Running them as separate CABasicAnimation objects added to the layer one after another won't guarantee synchronization. CAAnimationGroup solves this by acting as a container that manages a set of child animations as a single, atomic unit.


The Anatomy of a CAAnimationGroup

CAAnimationGroup is itself a subclass of CAAnimation, so you add it to a layer just like you would a CABasicAnimation. Its primary purpose is to hold and coordinate other animations.

Its most important property is:

  • animations: [CAAnimation]?: An array where you place the individual animation objects (CABasicAnimation, CAKeyframeAnimation, etc.) that you want to run together.

Like other animations, it also has properties like duration, repeatCount, and timingFunction. The group's duration is particularly important: it acts as the master duration for all animations within the group. If the child animations have their own durations set, they may be stretched or clipped to fit the group's duration. It's common practice to set the duration on the group and let the child animations inherit it.


Creating a Simple Animation Group

Let's look at a practical example of animating a view's scale and bounds simultaneously to create a "pulsing" effect.

Prototyping Animations in Swift

The article "Prototyping Animations in Swift" provides a great, simple example of why animation groups are necessary. It demonstrates animating concentric circles that expand outwards.

Read the first two paragraphs of the article, down to where the first GIF appears (the 'trippy' concentric circles). Pay attention to the reasoning: 'It’s important to animate the bounds in tandem with a scale transform... so let’s add both of these animations to an animation group to perform them at the same time.'

The core pattern for using CAAnimationGroup is straightforward:

  1. Create Individual Animations: Instantiate and configure each CABasicAnimation or CAKeyframeAnimation for the properties you want to change (e.g., opacity, transform, position).
  2. Create the Group: Instantiate CAAnimationGroup.
  3. Combine: Assign your array of individual animations to the group's animations property.
  4. Configure the Group: Set the group's duration and any other relevant properties.
  5. Update Model Layers: Just as we did in the last lesson, update the model layer's properties to their final values to prevent the "snap-back" effect.
  6. Add to Layer: Add the single CAAnimationGroup object to your layer.

Here is what that looks like in Swift code, creating an animation that causes a view to scale up and fade out at the same time:

// 1. Create individual animations
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = 1.0
scaleAnimation.toValue = 2.0

let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.fromValue = 1.0
fadeAnimation.toValue = 0.0

// 2. Create the group
let animationGroup = CAAnimationGroup()

// 3. Combine the animations
animationGroup.animations = [scaleAnimation, fadeAnimation]

// 4. Configure the group
animationGroup.duration = 1.5
animationGroup.timingFunction = CAMediaTimingFunction(name: .easeIn)

// 5. Update the model layer to the final state
// This is crucial to prevent the view from snapping back
myView.layer.transform = CATransform3DMakeScale(2.0, 2.0, 1.0)
myView.layer.opacity = 0.0

// 6. Add the group to the layer
myView.layer.add(animationGroup, forKey: "scaleAndFade")

A More Complex Example: Combining Animation Types

CAAnimationGroup isn't limited to just CABasicAnimation. You can mix and match different animation types to create very sophisticated effects.

Let's revisit the objc.io article from our previous lesson. It has an excellent section on CAAnimationGroup that demonstrates combining basic and keyframe animations for a card-shuffling effect.

Animations Explained - objc.io

The "Animations Explained" article from objc.io shows how to combine multiple animations for a shuffle effect.

Please read the section titled "Animation Groups". Notice how it combines a CABasicAnimation for zPosition with CAKeyframeAnimation objects for transform.rotation and position.

The code in the article is in Objective-C, which you should be able to parse given your experience. However, to make it crystal clear, here's a direct Swift translation of that shuffle animation code. This example animates zPosition, rotation, and position all at once.

// A CABasicAnimation for the z-axis position
let zPositionAnimation = CABasicAnimation(keyPath: "zPosition")
zPositionAnimation.fromValue = -1
zPositionAnimation.toValue = 1

// A CAKeyframeAnimation for rotation
let rotationAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
rotationAnimation.values = [0, 0.14, 0]
rotationAnimation.timingFunctions = [
    CAMediaTimingFunction(name: .easeInEaseOut),
    CAMediaTimingFunction(name: .easeInEaseOut)
]

// A CAKeyframeAnimation for position, moving along a path
let positionAnimation = CAKeyframeAnimation(keyPath: "position")
positionAnimation.values = [
    NSValue(cgPoint: CGPoint.zero),
    NSValue(cgPoint: CGPoint(x: 110, y: -20)),
    NSValue(cgPoint: CGPoint.zero)
]
positionAnimation.timingFunctions = [
    CAMediaTimingFunction(name: .easeInEaseOut),
    CAMediaTimingFunction(name: .easeInEaseOut)
]
positionAnimation.isAdditive = true // Animates relative to the layer's current position

// --- The Group ---
let group = CAAnimationGroup()
group.animations = [zPositionAnimation, rotationAnimation, positionAnimation]
group.duration = 1.2

// Update model layer to the final state
card.layer.zPosition = 1
// Note: For keyframe animations that end where they start, other model properties don't need updating.

// Add the group animation to the card's layer
card.layer.add(group, forKey: "shuffle")

This example beautifully illustrates the power of CAAnimationGroup as a coordinator for different types of animations, allowing you to build complex, layered visual effects.


Conclusion

This lesson completes our journey into the fundamentals of Core Animation. You started by learning to animate single properties and have now progressed to orchestrating multiple, complex animations in parallel.

Key Takeaways:

  • CAAnimationGroup is a container class used to run multiple CAAnimation objects simultaneously.
  • You create individual animations (like CABasicAnimation or CAKeyframeAnimation), place them in the group's animations array, and add the group to a layer.
  • The group's duration property acts as the master controller for the timing of all animations within it.
  • The rule from our last lesson still holds: you must update the model layer's properties to their final values to ensure the animated changes persist.

Preview of the Next Module:

So far, our animations have been "fire-and-forget." We define them, add them, and they run to completion. But what if you wanted an animation that a user could control with a gesture, scrubbing back and forth?

In our next module, "Interactive Animations and Transitions," we will explore precisely that. We'll start by learning about UIViewPropertyAnimator, a modern and powerful UIKit class that allows for creating interactive, interruptible, and reversible animations. This will bridge the gap between low-level Core Animation control and high-level, user-driven interactions.