Basic Keyframe Animations
Keyframe animations allow us to animate values, not views. These values can then be used to animate our views. With keyframes, we specify the key values and their duration, and the system takes care of interpolating these during the animation. At the moment of writing, there are four different keyframes:
- A
MoveKeyframeimmediately moves to the specified value. It's the only keyframe type without a duration. - A
LinearKeyframetakes a timing curve (of typeUnitCurve) and interpolates between values. - A
SpringKeyframedoes the same using a spring animation. - A
CubicKeyframeuses a Catmull–Rom spline to interpolate between multiple cubic keyframes in sequence.
Here is a view that wiggles when tapped. The wiggle is implemented by multiple keyframes. When we use CubicKeyframe, we can see that the system draws a smooth line through the specified points. With linear keyframes, the line also looks smooth, as subsequent keyframes are blended. However, when we look at the velocity, we can see it gets close to zero at the key points. We can also build this wiggle with phase animations or an Animatable modifier.
struct RotationWiggle: View { @State var trigger = 0 var body: some View { RoundedRectangle(cornerRadius: 12) .fill(.red) .frame(width: 80, height: 96) .onTapGesture { trigger += 1 } .keyframeAnimator( initialValue: 0.0, trigger: trigger ) { rectangle, angle in rectangle .rotationEffect(.degrees( angle )) } keyframes: { _ in KeyframeTrack(\.self) { CubicKeyframe(-15, duration: 0.1) CubicKeyframe(12, duration: 0.15) CubicKeyframe(-8, duration: 0.15) CubicKeyframe(4, duration: 0.15) CubicKeyframe(0, duration: 0.1) } } }}
Drag points vertically to change values. Drag the lower handles to change timing. Arrow keys edit a selected point; hold Shift for larger steps. Delete removes an interior point.
6 points.One of the most useful features of keyframes is that we can have multiple tracks. In the example below, we have a classic bouncing ball animation. At rest, the ball is bottom-aligned in the frame. We animate the vertical scale and vertical offset and apply it to the view using keyframeAnimator. The keyframes are defined below.
struct AnimationValues { var verticalOffset = 0.0 var verticalScale = 1.0}struct BouncingBall: View { @State var trigger = 0 var body: some View { Circle() .fill(.red) .frame(width: 56, height: 56) .onTapGesture { trigger += 1 } .keyframeAnimator( initialValue: AnimationValues(), trigger: trigger ) { ball, value in ball .scaleEffect( x: 1 / value.verticalScale, y: value.verticalScale, anchor: .bottom ) .offset( y: value.verticalOffset ) } keyframes: { _ in keyframes } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) .overlay(alignment: .bottom) { Rectangle() .fill(.tertiary) .frame(height: 1) } }}
We use linear keyframes for the vertical offset and cubic keyframes for the vertical scale. You can play around with the values in the diagram (change the duration and scalar values) to see how they affect the animation.
@KeyframesBuilder<AnimationValues>var keyframes: some Keyframes<AnimationValues> { KeyframeTrack(\.verticalOffset) { LinearKeyframe(0, duration: 0.15) LinearKeyframe(-140, duration: 0.4, timingCurve: .easeOut) LinearKeyframe(0, duration: 0.4, timingCurve: .easeIn) LinearKeyframe(0, duration: 0.35) } KeyframeTrack(\.verticalScale) { CubicKeyframe(0.7, duration: 0.15) CubicKeyframe(1.15, duration: 0.15) CubicKeyframe(1, duration: 0.25) CubicKeyframe(1.15, duration: 0.4) CubicKeyframe(0.65, duration: 0.08) CubicKeyframe(1, duration: 0.12) LinearKeyframe(1, duration: 0.15) }}
Drag points vertically to change values. Drag the lower handles to change timing. Arrow keys edit a selected point; hold Shift for larger steps. Delete removes an interior point.
5 points.Drag points vertically to change values. Drag the lower handles to change timing. Arrow keys edit a selected point; hold Shift for larger steps. Delete removes an interior point.
8 points.Resources

Four Ways to Shake compares keyframe and phase animations, including their velocity curves.
https://chris.eidhof.nl/post/four-ways-to-shake/
Building Keyframe Animations on Swift Talk: part 1 (#366), part 2 (#367), and part 3 (#368).
https://talk.objc.io/episodes/S01E366-building-keyframe-animations-part-1
Cubic Bézier Keyframes on Swift Talk: part 1 (#369), part 2 (#370), part 3 (#371), part 4 (#372), and part 5 (#373).
https://talk.objc.io/episodes/S01E373-cubic-bezier-keyframes-part-5
Apple's Wind your way through advanced animations in SwiftUI (WWDC23). The keyframes section starts at 8:12.
https://developer.apple.com/videos/play/wwdc2023/10157/
Apple's Explore SwiftUI animation (WWDC23).
https://developer.apple.com/videos/play/wwdc2023/10156/
Thinking in SwiftUI, Animations chapter: “Keyframe-Based Animations.”
https://www.objc.io/books/thinking-in-swiftui/