跳到主要内容

动画

问题

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 性能相当。

相关链接