iOS Animation Implementation
Write animation code that uses Apple's frameworks directly. Third-party animation libraries add dependency risk and often lag behind new OS releases — Apple's APIs are well-optimized for the render pipeline and get free improvements with each iOS version.
Before Writing Custom Animation
Check whether the system already handles the motion you need. Apple's HIG: "Many system components automatically include motion, letting you offer familiar and consistent experiences throughout your app." System components also automatically adjust for accessibility settings and input methods — Liquid Glass (iOS 26) responds with greater emphasis to direct touch and produces subdued effects for trackpad. Custom animation can't match this adaptiveness for free, so prefer system-provided motion when it exists.
Skip custom animation when:
- - Standard navigation transitions cover your case (push, pop, sheet, fullScreenCover)
- SF Symbol
.symbolEffect provides the feedback you need - INLINECODE1 handles your data change
- The system's default spring on
withAnimation is sufficient
Write custom animation when:
- - The system doesn't provide the spatial relationship you need (hero transitions, custom gestures)
- You need coordinated multi-property choreography
- The animation is a signature moment that defines the app's identity
- Gesture-driven interaction requires custom progress mapping
API Selection
Choose the right API for the job. Start with SwiftUI animations (simplest, most declarative), drop to UIKit when you need interactive control, and reach for Core Animation only when you need layer-level precision.
| Need | API | Why |
|---|
| State-driven property changes | INLINECODE3 / INLINECODE4 | Declarative, automatic interpolation |
| Multi-step sequenced animation |
PhaseAnimator | Discrete phases with per-phase timing |
| Per-property timeline control |
KeyframeAnimator | Independent keyframe tracks per property |
| Hero transitions between views |
matchedGeometryEffect +
Namespace | Geometry matching across view identity |
| Navigation push/pop with zoom |
.navigationTransition(.zoom) | iOS 18+ built-in zoom transition |
| Custom view insertion/removal |
Transition protocol conformance |
TransitionPhase-based modifier |
| In-view content swap |
.contentTransition() | Numeric text, interpolation, opacity |
| Scroll-position-based effects |
.scrollTransition | Phase-driven scroll-linked animation |
| SF Symbol animation |
.symbolEffect() | Bounce, pulse, wiggle, breathe, rotate |
| Interactive/interruptible (UIKit) |
UIViewPropertyAnimator | Pause, resume, reverse, scrub |
| Per-layer property animation |
CABasicAnimation /
CASpringAnimation | Shadow, border, cornerRadius animation |
| Complex choreography (layers) |
CAKeyframeAnimation +
CAAnimationGroup | Multi-property layer animation |
| Physics simulation |
UIDynamicAnimator | Gravity, collision, snap, attachment |
| Haptic feedback paired with animation |
.sensoryFeedback modifier | Tied to value changes |
| Animated background gradients |
MeshGradient | 2D grid of positioned, animated colors |
Implementation by Category
Detailed patterns and code examples live in the reference files. Load the one that matches your task:
| Task | Reference |
|---|
| SwiftUI declarative animations (withAnimation, springs, phase, keyframe) | references/swiftui-animations.md |
| View transitions (navigation, modal, custom Transition protocol) |
references/transitions.md |
| Gesture-driven interactive animations |
references/gesture-animations.md |
| Core Animation and UIKit animation patterns |
references/core-animation.md |
When to Load References
- - Writing
withAnimation, spring parameters, PhaseAnimator, or KeyframeAnimator → swiftui-animations.md - Building navigation transitions, modal presentations,
matchedGeometryEffect, or custom Transition → transitions.md - Implementing drag-to-dismiss, swipe actions, pinch/rotate, or scroll-linked effects → gesture-animations.md
- Working with
CABasicAnimation, UIViewPropertyAnimator, layer animations, or bridging SwiftUI↔UIKit → core-animation.md
Spring Parameters Quick Reference
Springs are the default animation type in modern SwiftUI. Use duration and bounce — not mass/stiffness/damping unless bridging to UIKit/CA.
| Preset | Duration | Bounce | Use Case |
|---|
| INLINECODE32 | 0.5 | 0.0 | Default transitions, most state changes |
| INLINECODE33 |
0.3 | 0.15 | Micro-interactions, toggles, quick feedback |
|
.bouncy | 0.5 | 0.3 | Playful moments, attention-drawing |
|
.interactiveSpring | 0.15 | 0.0 | Gesture tracking, drag following |
| Custom | varies | varies |
.spring(duration: 0.4, bounce: 0.2) |
Accessibility & Multimodal Feedback
Apple's HIG: "Make motion optional" and "supplement visual feedback by also using alternatives like haptics and audio to communicate." Every animation must handle Reduce Motion, and important state changes should use multiple feedback channels — not animation alone.
CODEBLOCK0
Reduce Motion fallback options (from most to least graceful):
- 1. Crossfade — replace motion with opacity transition
- Shortened — same animation, much faster (0.1–0.15s), no bounce
- Instant —
.animation(.none) or skip the animation block entirely
Cancellation & Interruptibility
Apple's HIG: "Don't make people wait for an animation to complete before they can do anything, especially if they have to experience the animation more than once." Every animation must be interruptible.
- - Spring animations retarget automatically — this is the default and almost always what you want
- For gesture-driven animations, the user is always in control — let them cancel mid-flight
- For sequenced animations (KeyframeAnimator, PhaseAnimator with trigger), ensure the UI remains interactive during playback
- Never disable user interaction during an animation unless there's a critical reason (e.g., destructive action confirmation)
Performance Checklist
- - Animate on the render server when possible — Core Animation runs off the main thread, SwiftUI's
drawingGroup() moves rendering to Metal - Avoid animating view identity changes (
.id() modifier) — this destroys and recreates the view - Use
geometryGroup() when parent geometry changes cause child layout anomalies during animation - Provide explicit
shadowPath when animating shadows — without it, the system recalculates the path every frame - In lists and scroll views, avoid per-item blur/shadow animations — these cause offscreen rendering for each cell
- Keep
PhaseAnimator and looping animations lightweight — they run continuously - For frequent interactions, prefer system-provided animation over custom motion — Apple's HIG: "generally avoid adding motion to UI interactions that occur frequently"
- Profile with Instruments → "Animation Hitches" template to find frame drops
iOS 动画实现
编写直接使用 Apple 框架的动画代码。第三方动画库会增加依赖风险,并且往往滞后于新系统版本——Apple 的 API 针对渲染管线进行了充分优化,并且每个 iOS 版本都会获得免费改进。
在编写自定义动画之前
检查系统是否已经处理了你需要的动效。Apple 的 HIG(人机界面指南)指出:许多系统组件会自动包含动效,让你在整个应用中提供熟悉且一致的体验。系统组件还会自动适配无障碍设置和输入方式——Liquid Glass(iOS 26)对直接触摸会做出更强烈的响应,而对触控板则产生柔和的效果。自定义动画无法免费实现这种自适应能力,因此当系统提供动效时,优先使用系统提供的动效。
在以下情况下跳过自定义动画:
- - 标准导航转场覆盖了你的场景(push、pop、sheet、fullScreenCover)
- SF Symbol 的 .symbolEffect 提供了你需要的反馈
- .contentTransition(.numericText) 处理了你的数据变化
- 系统在 withAnimation 上的默认弹簧动画已经足够
在以下情况下编写自定义动画:
- - 系统没有提供你需要的空间关系(英雄转场、自定义手势)
- 你需要协调的多属性编排
- 动画是定义应用身份的标志性时刻
- 手势驱动的交互需要自定义进度映射
API 选择
为任务选择合适的 API。从 SwiftUI 动画开始(最简单、最声明式),当需要交互控制时降级到 UIKit,只有在需要图层级精度时才使用 Core Animation。
| 需求 | API | 原因 |
|---|
| 状态驱动的属性变化 | withAnimation / .animation(_:value:) | 声明式,自动插值 |
| 多步骤序列动画 |
PhaseAnimator | 离散阶段,每阶段独立计时 |
| 逐属性时间线控制 | KeyframeAnimator | 每个属性独立的关键帧轨道 |
| 视图间的英雄转场 | matchedGeometryEffect + Namespace | 跨视图身份的几何匹配 |
| 带缩放的导航 push/pop | .navigationTransition(.zoom) | iOS 18+ 内置缩放转场 |
| 自定义视图插入/移除 | Transition 协议遵循 | 基于 TransitionPhase 的修饰符 |
| 视图内内容切换 | .contentTransition() | 数字文本、插值、透明度 |
| 基于滚动位置的效果 | .scrollTransition | 阶段驱动的滚动关联动画 |
| SF Symbol 动画 | .symbolEffect() | 弹跳、脉冲、摆动、呼吸、旋转 |
| 交互式/可中断(UIKit) | UIViewPropertyAnimator | 暂停、恢复、反转、擦洗 |
| 逐图层属性动画 | CABasicAnimation / CASpringAnimation | 阴影、边框、圆角动画 |
| 复杂编排(图层) | CAKeyframeAnimation + CAAnimationGroup | 多属性图层动画 |
| 物理模拟 | UIDynamicAnimator | 重力、碰撞、吸附、附着 |
| 与动画配对的触觉反馈 | .sensoryFeedback 修饰符 | 绑定到值变化 |
| 动画背景渐变 | MeshGradient | 定位彩色点的二维网格动画 |
按类别实现
详细的模式和代码示例位于参考文件中。加载与你任务匹配的文件:
references/transitions.md |
| 手势驱动的交互式动画 |
references/gesture-animations.md |
| Core Animation 和 UIKit 动画模式 |
references/core-animation.md |
何时加载参考文件
- - 编写 withAnimation、弹簧参数、PhaseAnimator 或 KeyframeAnimator → swiftui-animations.md
- 构建导航转场、模态呈现、matchedGeometryEffect 或自定义 Transition → transitions.md
- 实现拖拽关闭、滑动操作、捏合/旋转或滚动关联效果 → gesture-animations.md
- 处理 CABasicAnimation、UIViewPropertyAnimator、图层动画或桥接 SwiftUI↔UIKit → core-animation.md
弹簧参数快速参考
弹簧动画是现代 SwiftUI 中的默认动画类型。使用 duration 和 bounce——而不是质量/刚度/阻尼,除非桥接到 UIKit/CA。
| 预设 | 持续时间 | 弹跳 | 使用场景 |
|---|
| .smooth | 0.5 | 0.0 | 默认转场,大多数状态变化 |
| .snappy |
0.3 | 0.15 | 微交互、开关、快速反馈 |
| .bouncy | 0.5 | 0.3 | 趣味时刻、吸引注意力 |
| .interactiveSpring | 0.15 | 0.0 | 手势跟踪、拖拽跟随 |
| 自定义 | 可变 | 可变 | .spring(duration: 0.4, bounce: 0.2) |
无障碍与多模态反馈
Apple 的 HIG 指出:让动效成为可选项以及通过使用触觉和音频等替代方式来补充视觉反馈。每个动画都必须处理减少动效设置,重要的状态变化应使用多种反馈渠道——而不仅仅是动画本身。
swift
@Environment(\.accessibilityReduceMotion) private var reduceMotion
// 模式 1:条件动画
withAnimation(reduceMotion ? .none : .spring()) {
isExpanded.toggle()
}
// 模式 2:简化替代方案
.animation(reduceMotion ? .easeOut(duration: 0.15) : .spring(duration: 0.5, bounce: 0.3), value: isActive)
// 模式 3:完全跳过
if !reduceMotion {
view.phaseAnimator(phases) { / ... / }
}
减少动效的备选方案(从最优雅到最不优雅):
- 1. 交叉淡入淡出——用透明度转场替代动效
- 缩短——相同动画,速度更快(0.1–0.15秒),无弹跳
- 即时——.animation(.none) 或完全跳过动画块
取消与可中断性
Apple 的 HIG 指出:不要让用户等待动画完成才能做任何事情,特别是当他们需要多次体验该动画时。每个动画都必须是可中断的。
- - 弹簧动画会自动重新定位目标——这是默认行为,几乎总是你想要的
- 对于手势驱动的动画,用户始终处于控制地位——让他们能在中途取消
- 对于序列动画(KeyframeAnimator、带触发器的 PhaseAnimator),确保 UI 在播放期间保持可交互
- 除非有关键原因(例如,破坏性操作确认),否则永远不要在动画期间禁用用户交互
性能检查清单
- - 尽可能在渲染服务器上运行动画——Core Animation 在主线程外运行,SwiftUI 的 drawingGroup() 将渲染移至 Metal
- 避免动画化视图身份变化(.id() 修饰符)——这会销毁并重新创建视图
- 当父视图几何变化在动画期间导致子视图布局异常时,使用 geometryGroup()
- 在动画化阴影时提供显式的 shadowPath——没有它,系统每帧都会重新计算路径
- 在列表和滚动视图中,避免对每个项目使用模糊/阴影动画——这会导致每个单元格进行离屏渲染
- 保持 PhaseAnimator 和循环动画轻量——它们会持续运行
- 对于频繁交互,优先使用系统提供的动画而非自定义动效——Apple 的 HIG 指出:通常避免为频繁发生的 UI 交互添加动效
- 使用 Instruments → Animation Hitches 模板进行分析,以发现帧率下降