Animation Patterns
Correct API shapes and patterns for SwiftUI animations. Prevents the most common mistakes: mixed spring parameter generations, wrong PhaseAnimator/KeyframeAnimator closure signatures, and using matchedGeometryEffect where matchedTransitionSource is needed.
When This Skill Activates
Use this skill when the user:
- Asks to add, fix, or review animation code
- Mentions spring, bounce, or snappy animations
- Wants view transitions (insertion/removal, hero, zoom)
- Asks about PhaseAnimator or KeyframeAnimator
- Wants SF Symbol effects (bounce, pulse, wiggle, breathe)
- Mentions matchedGeometryEffect or matchedTransitionSource
- Asks about reduce motion / animation accessibility
- Wants to sequence or chain animations
- Mentions withAnimation, animation completions, or Transaction
- Wants scroll-driven effects (parallax, carousels,
scrollTransition,visualEffect) - Mentions MeshGradient, TextRenderer, or Metal shader effects (
colorEffect,distortionEffect,layerEffect)
Decision Tree
Choose the right reference file based on what the user needs:
What are you animating?
β
ββ A state change (opacity, position, color)
β ββ β core-animations.md
β ββ withAnimation { } β explicit animation
β ββ .animation(_:value:) β implicit animation
β ββ Spring configuration β .spring, .bouncy, .snappy, .smooth
β
ββ A multi-step / sequenced animation
β ββ β phase-keyframe-animators.md (PhaseAnimator)
β ββ Cycles through discrete phases automatically or on trigger
β
ββ A complex multi-property animation (scale + rotation + offset)
β ββ β phase-keyframe-animators.md (KeyframeAnimator)
β ββ Timeline-based keyframes with per-property tracks
β
ββ A view appearing / disappearing
β ββ β transitions.md
β ββ .transition() β insertion/removal
β ββ .contentTransition() β text/symbol changes
β ββ .asymmetric() β different in/out
β
ββ A hero / zoom navigation transition
β ββ β transitions.md (matchedTransitionSource section)
β ββ iOS 18+: matchedTransitionSource + .navigationTransition(.zoom)
β ββ UIKit: preferredTransition = .zoom (capture stable IDs, never views)
β ββ iOS 14+: matchedGeometryEffect (NOT for NavigationStack)
β
ββ A UIKit view driven by SwiftUI state or gestures
β ββ β transitions.md (Bridging UIKit and SwiftUI Animations)
β ββ UIView.animate(.spring(...)) / context.animate in updateUIView
β
ββ An SF Symbol animation
β ββ β symbol-effects.md
β ββ .symbolEffect(.bounce), .pulse, .wiggle, .breathe, .rotate
β
ββ A scroll-driven, geometry-driven, or shader effect
β ββ β visual-effects.md
β ββ .scrollTransition β carousel scale/parallax/caption fades
β ββ .visualEffect β position-based effects without GeometryReader
β ββ MeshGradient β animatable multi-point gradients
β ββ TextRenderer β per-line / per-glyph text animation
β ββ ShaderLibrary + .colorEffect / .distortionEffect / .layerEffect
β
ββ Spring physics / timing configuration
ββ β core-animations.md (Spring Configurations section)API Availability
| API | Minimum Version | Reference |
|---|---|---|
withAnimation |
iOS 13 | core-animations.md |
.animation(_:value:) |
iOS 13 | core-animations.md |
.spring(response:dampingFraction:) |
iOS 13 | core-animations.md |
.matchedGeometryEffect |
iOS 14 | transitions.md |
.transition(.push(from:)) |
iOS 16 | transitions.md |
.contentTransition(.numericText()) |
iOS 16 | transitions.md |
PhaseAnimator |
iOS 17 | phase-keyframe-animators.md |
KeyframeAnimator |
iOS 17 | phase-keyframe-animators.md |
.spring(duration:bounce:) |
iOS 17 | core-animations.md |
Spring presets (.bouncy, .snappy, .smooth) |
iOS 17 | core-animations.md |
withAnimation(_:completionCriteria:_:completion:) |
iOS 17 | core-animations.md |
.symbolEffect() |
iOS 17 | symbol-effects.md |
.transition(.blurReplace) |
iOS 17 | transitions.md |
.contentTransition(.symbolEffect(.replace)) |
iOS 17 | transitions.md |
Transition protocol / TransitionPhase |
iOS 17 | transitions.md |
TransactionKey, scoped .animation / .transaction variants |
iOS 17 | core-animations.md |
KeyframeTimeline, .mapCameraKeyframeAnimator |
iOS 17 | phase-keyframe-animators.md |
.scrollTransition, .visualEffect |
iOS 17 | visual-effects.md |
Shaders: .colorEffect / .distortionEffect / .layerEffect |
iOS 17 | visual-effects.md |
MeshGradient |
iOS 18 | visual-effects.md |
TextRenderer / TextAttribute |
iOS 18 | visual-effects.md |
.matchedTransitionSource |
iOS 18 | transitions.md |
.navigationTransition(.zoom) β push, sheet, fullScreenCover |
iOS 18 | transitions.md |
UIViewController.preferredTransition = .zoom |
iOS 18 | transitions.md |
UIView.animate(_: Animation) / context.animate |
iOS 18 | transitions.md |
Top 5 Mistakes β Quick Reference
| # | Mistake | Fix | Details |
|---|---|---|---|
| 1 | spring(response:bounce:) β mixing parameter generations |
Use either spring(response:dampingFraction:) (iOS 13) or spring(duration:bounce:) (iOS 17) |
core-animations.md |
| 2 | .animation(.spring()) without value: parameter |
Always pass value: β the no-value variant is deprecated (iOS 15) |
core-animations.md |
| 3 | Wrong PhaseAnimator closure signature | PhaseAnimator(phases) { content, phase in } β not { phase in } |
phase-keyframe-animators.md |
| 4 | Using matchedGeometryEffect for NavigationStack transitions |
Use matchedTransitionSource + .navigationTransition(.zoom) on iOS 18+ |
transitions.md |
| 5 | Using withAnimation for SF Symbol effects |
Use .symbolEffect() modifier instead |
symbol-effects.md |
Review Checklist
When reviewing animation code, verify:
- Reduce motion β animations respect
AccessibilityMotionEffectorUIAccessibility.isReduceMotionEnabled; provide non-motion alternatives - Duration limits β no animation exceeds ~0.5s for UI feedback; longer only for decorative/ambient effects
- Spring vs linear β springs for interactive/physical motion; linear/easeInOut only for opacity fades or progress indicators
- No deprecated APIs β
.animation(.spring())withoutvalue:is deprecated;.animation(nil)is replaced bywithTransaction - Correct spring generation β parameter names match the same API generation (never mix
responsewithbounce) - Completion handlers β using
withAnimation(_:completionCriteria:_:completion:)(iOS 17+), not inventing.onAnimationCompleted - Transition scope β
.transition()only affects views insideif/switchcontrolled by state; not for views that are always present - Per-frame closures stay cheap β KeyframeAnimator
contentclosures,visualEffectclosures,TextRenderer.draw, andAnimatableview bodies run every frame; no allocation, formatting, or layout math inside
Reference Files
| File | Content |
|---|---|
| core-animations.md | withAnimation, springs, completions, transactions + TransactionKey, timing curves, Animatable mechanics, fluid-interface principles (momentum projection, rubber-banding, hysteresis) |
| phase-keyframe-animators.md | PhaseAnimator, KeyframeAnimator, KeyframeTimeline, MapKit camera keyframes, custom animations |
| transitions.md | View transitions, custom Transition protocol, matched geometry, navigation/zoom transitions (SwiftUI + UIKit), interruptibility, UIKitβSwiftUI bridging, gesture-driven springs |
| symbol-effects.md | SF Symbol effects, accessibility |
| visual-effects.md | scrollTransition, visualEffect, MeshGradient, TextRenderer, shader effects (colorEffect/distortionEffect/layerEffect), effect pipelines, semantic alignment guides |