home / skills / rshankras / claude-code-apple-skills / animation-patterns

animation-patterns skill

/skills/design/animation-patterns

This skill helps you implement, review, and fix SwiftUI animation patterns, improving transitions, PhaseAnimator and KeyframeAnimator workflows, and symbol

npx playbooks add skill rshankras/claude-code-apple-skills --skill animation-patterns

Review the files below or copy the command above to add this skill to your agents.

Files (5)
SKILL.md
6.0 KB
---
name: animation-patterns
description: SwiftUI animation patterns including springs, transitions, PhaseAnimator, KeyframeAnimator, and SF Symbol effects. Use when implementing, reviewing, or fixing animation code on iOS/macOS.
allowed-tools: [Read, Glob, Grep]
---

# 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**

## 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)
│     └─ iOS 14+: matchedGeometryEffect (NOT for NavigationStack)
│
├─ An SF Symbol animation
│  └─ → symbol-effects.md
│     └─ .symbolEffect(.bounce), .pulse, .wiggle, .breathe, .rotate
│
└─ 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 |
| `.matchedTransitionSource` | iOS 18 | transitions.md |
| `.navigationTransition(.zoom)` | 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 `AccessibilityMotionEffect` or `UIAccessibility.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())` without `value:` is deprecated; `.animation(nil)` is replaced by `withTransaction`
- [ ] **Correct spring generation** — parameter names match the same API generation (never mix `response` with `bounce`)
- [ ] **Completion handlers** — using `withAnimation(_:completionCriteria:_:completion:)` (iOS 17+), not inventing `.onAnimationCompleted`
- [ ] **Transition scope** — `.transition()` only affects views inside `if`/`switch` controlled by state; not for views that are always present

## Reference Files

| File | Content |
|------|---------|
| [core-animations.md](core-animations.md) | withAnimation, springs, completions, transactions, timing curves |
| [phase-keyframe-animators.md](phase-keyframe-animators.md) | PhaseAnimator, KeyframeAnimator, custom animations |
| [transitions.md](transitions.md) | View transitions, matched geometry, navigation transitions |
| [symbol-effects.md](symbol-effects.md) | SF Symbol effects, accessibility |

Overview

This skill provides practical SwiftUI animation patterns and API guidance for iOS and macOS. It focuses on correct spring configurations, view transitions, PhaseAnimator/KeyframeAnimator usage, and SF Symbol effects to avoid common mistakes and accessibility regressions.

How this skill works

The skill inspects animation intents and code snippets to recommend the correct API shapes and parameter generations. It detects misuse such as mixed spring parameter styles, wrong closure signatures for PhaseAnimator/KeyframeAnimator, improper transition primitives, and suggestions for reduce-motion handling.

When to use it

  • Adding, fixing, or reviewing SwiftUI animation code
  • Implementing springy, bouncy, or snappy motion
  • Creating view insertion/removal or hero/zoom navigation transitions
  • Sequencing or keyframing multi-step animations
  • Applying SF Symbol effects like bounce, pulse, or wiggle

Best practices

  • Use either the iOS 13 spring API (response+dampingFraction) or the iOS 17 spring API (duration+bounce); never mix parameters
  • Prefer .symbolEffect() for SF Symbol animations instead of withAnimation for better system integration
  • Always include value: when using .animation(_:value:) to avoid deprecated no-value variants
  • Respect reduce-motion settings and provide non-motion alternatives for critical UI changes
  • Keep UI feedback animations short (~0.5s) unless intentionally decorative

Example use cases

  • Fixing a spring animation that mixes response/dampingFraction with duration/bounce
  • Converting a navigation hero animation from matchedGeometryEffect to matchedTransitionSource + .navigationTransition(.zoom) on iOS 18+
  • Defining a PhaseAnimator with the correct closure signature: PhaseAnimator(phases) { content, phase in ... }
  • Sequencing a multi-property motion with KeyframeAnimator timelines for scale, rotation, and offset
  • Adding a pulse or wiggle to an SF Symbol using .symbolEffect(.pulse) while honoring reduce-motion

FAQ

When should I use PhaseAnimator vs KeyframeAnimator?

Use PhaseAnimator for discrete phase-driven cycles or toggles. Use KeyframeAnimator for timeline-based, per-property keyframes and complex multi-property choreography.

How do I handle accessibility reduce motion?

Query the system reduce-motion setting and provide instant state changes or simplified fades. Prefer alternatives like opacity changes instead of heavy motion when reduce motion is enabled.