动画
问题
SwiftUI 的动画机制是什么?显式动画和隐式动画的区别?
答案
隐式动画 vs 显式动画
// 隐式动画:附加在 View 上,属性变化自动触发
Text("Hello")
.offset(y: moved ? 100 : 0)
.animation(.spring(), value: moved)
// 显式动画:包裹状态变更
Button("Move") {
withAnimation(.easeInOut(duration: 0.3)) {
moved.toggle()
}
}
动画类型
.animation(.linear(duration: 0.3), value: x) // 匀速
.animation(.easeIn(duration: 0.3), value: x) // 渐入
.animation(.easeOut(duration: 0.3), value: x) // 渐出
.animation(.easeInOut(duration: 0.3), value: x) // 渐入渐出
.animation(.spring(response: 0.5, dampingFraction: 0.6), value: x) // 弹簧
.animation(.interpolatingSpring(stiffness: 100, damping: 10), value: x)
Transition(视图出现/消失动画)
if showDetail {
DetailView()
.transition(.slide) // 滑入滑出
.transition(.opacity) // 淡入淡出
.transition(.scale.combined(with: .opacity)) // 组合
.transition(.asymmetric(insertion: .slide, removal: .opacity)) // 不对称
}
matchedGeometryEffect(共享元素动画)
@Namespace private var animation
// 列表中的小图
Image("photo")
.matchedGeometryEffect(id: "hero", in: animation)
.onTapGesture { withAnimation { showDetail = true } }
// 详情页的大图
if showDetail {
Image("photo")
.matchedGeometryEffect(id: "hero", in: animation)
.onTapGesture { withAnimation { showDetail = false } }
}
PhaseAnimator(iOS 17+)
多阶段自动循环动画:
PhaseAnimator([false, true]) { value in
Image(systemName: "heart.fill")
.scaleEffect(value ? 1.2 : 1.0)
.foregroundColor(value ? .red : .pink)
} animation: { _ in
.easeInOut(duration: 0.5)
}
常见面试问题
Q1: 为什么推荐显式动画(withAnimation)而非隐式动画?
答案:显式动画可以精确控制哪些状态变化需要动画,避免意外的动画效果。隐式动画 .animation 会对所有能触发该 View 刷新的属性变化都执行动画,可能产生非预期效果。
Q2: SwiftUI 动画的底层是什么?
答案:SwiftUI 动画底层使用 Core Animation。SwiftUI 计算出动画的起止状态,生成 CAAnimation 提交到渲染服务器,在 GPU 上执行。所以 SwiftUI 的动画性能和 UIKit 的 Core Animation 性能相当。