07. 动画实战
动画的关键是:用对动画类型、避免布局抖动、保证动画状态一致。下面按常用场景展开。
一、UIView 动画(最常用)
UIView.animate(withDuration: 0.25,
delay: 0,
options: [.curveEaseInOut]) {
self.cardView.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
self.cardView.alpha = 0.8
} completion: { _ in
self.cardView.transform = .identity
self.cardView.alpha = 1
}
适合透明度、位移、缩放、旋转等 UI 变化。
二、弹簧动画
UIView.animate(withDuration: 0.6,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5,
options: []) {
self.panel.transform = .identity
}
usingSpringWithDamping 越小弹性越强。
三、Core Animation(Layer 级动画)
1. 基础动画
let anim = CABasicAnimation(keyPath: "opacity")
anim.fromValue = 0
anim.toValue = 1
anim.duration = 0.3
layer.opacity = 1 // 必须更新最终状态
layer.add(anim, forKey: "fadeIn")
注意:动画只作用于“展示层”,最终状态要写回 layer。
2. 关键帧动画
let anim = CAKeyframeAnimation(keyPath: "position")
anim.values = [
CGPoint(x: 40, y: 40),
CGPoint(x: 80, y: 60),
CGPoint(x: 120, y: 40)
]
anim.duration = 0.5
anim.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
layer.add(anim, forKey: "pathMove")
四、UIViewPropertyAnimator(交互动画)
let animator = UIViewPropertyAnimator(duration: 0.4, curve: .easeInOut) {
self.drawerView.transform = CGAffineTransform(translationX: 0, y: -300)
}
animator.startAnimation()
animator.pauseAnimation()
animator.fractionComplete = 0.5
animator.continueAnimation(withTimingParameters: nil, durationFactor: 0.6)
适合手势驱动的动画,如抽屉、卡片拖拽。
五、避免动画抖动
- 动画中不要频繁触发布局
- 使用
transform优先于修改frame - 列表内动画尽量简化
// 不建议频繁改 frame
// 建议改 transform
view.transform = CGAffineTransform(translationX: 0, y: -12)
六、交互手势 + 动画结合
@objc func handlePan(_ pan: UIPanGestureRecognizer) {
let translation = pan.translation(in: view)
let progress = min(max(translation.y / 300, 0), 1)
switch pan.state {
case .changed:
animator.fractionComplete = progress
case .ended, .cancelled:
animator.continueAnimation(withTimingParameters: nil, durationFactor: 1)
default:
break
}
}
七、动画性能要点
- 优先使用
opacity和transform - 避免在动画中触发大量重绘
- 对复杂阴影或蒙版保持谨慎
动画写得稳定,体验会非常明显。关键是让“动画驱动状态”,而不是“动画制造状态”。