What You Will Build
A star icon that bounces upward, scales, wiggles, and casts a blurred reflection — all using the iOS 17 keyframeAnimator API. This is the reward animation you see in games, achievement unlocks, and gamified apps.
Why This Pattern Matters
Before iOS 17, multi-property keyframe animations required chaining withAnimation blocks with manual delays. The new keyframeAnimator API lets you define parallel tracks for different properties with different timing curves, all in one declarative block.
Step 1: Define Animated Properties
private struct KeyframeValues {
var scale: CGFloat = 1
var offsetY: CGFloat = 0
var rotation: Angle = .zero
var reflectionOpacity: CGFloat = 0.5
}
Step 2: Apply the Keyframe Animator
Image(systemName: "star.fill")
.font(.system(size: 80))
.foregroundStyle(.yellow)
.keyframeAnimator(
initialValue: KeyframeValues(),
trigger: animationTrigger
) { view, frame in
view
.scaleEffect(frame.scale)
.rotationEffect(frame.rotation, anchor: .bottom)
.offset(y: frame.offsetY)
} keyframes: { _ in
KeyframeTrack(\.offsetY) {
CubicKeyframe(10, duration: 0.15)
SpringKeyframe(-100, duration: 0.3, spring: .bouncy)
CubicKeyframe(-100, duration: 0.45)
SpringKeyframe(0, duration: 0.3, spring: .bouncy)
}
KeyframeTrack(\.scale) {
CubicKeyframe(1.0, duration: 0.15)
CubicKeyframe(1.8, duration: 0.3)
CubicKeyframe(1.8, duration: 0.45)
CubicKeyframe(1.0, duration: 0.3)
}
KeyframeTrack(\.rotation) {
CubicKeyframe(.zero, duration: 0.45)
CubicKeyframe(.degrees(15), duration: 0.1)
CubicKeyframe(.degrees(-15), duration: 0.1)
CubicKeyframe(.zero, duration: 0.15)
}
}
Tips and Pitfalls
- iOS 17+ only. For older targets, use chained
withAnimation. - Trigger must be Equatable. Toggling a Bool is simplest.
- Tracks run independently — durations need not match.
iOS Version: iOS 17+