UIKit Dynamics — iOS (UIKit) 中的物理动画框架,允许View根据真实物理定律进行交互:重力、碰撞、弹性和引力。本文解释了UIDynamicAnimator的组件、行为(behavior)的类型以及实际应用场景。
要点
UIKit Dynamics — 由Apple在iOS 7(2013)中引入的物理建模框架。与UIView.animate(线性过渡)和Core Animation(图层动画)不同,UIKit Dynamics模拟真实的物理交互:重力、碰撞、力、弹性和摩擦力。开发者将行为(behaviors)添加到UIDynamicAnimator,系统自动为每一帧计算物理效果。
该框架以60/120 FPS运行,并使用基于刚体模拟(rigid body simulation)的内置物理引擎。UIKit Dynamics不需要物理或数学知识——只需应用现成的行为并配置参数即可。
根据Apple的说法,UIKit Dynamics适用于UI效果(卡堆动画、图标「抖动」、界面物理),但不适用于游戏——游戏请使用SceneKit或SpriteKit。
UIDynamicAnimator — UIKit Dynamics的核心类。它使用reference view(通常是控制器的view)初始化,并管理所有活跃的行为。每一帧,UIDynamicAnimator根据物理定律重新计算所有添加元素的位置、速度和加速度。
addBehavior方法添加一个行为(或复杂的UIDynamicBehavior),removeBehavior — 移除。当所有对象达到静止状态(没有碰撞和力)时,animator自动暂停。委托方法 — UIDynamicAnimatorDelegate — 通知模拟的暂停和恢复。
每个参与物理的对象必须实现UIDynamicItem协议。UIView和UICollectionViewLayoutAttributes已经实现了它。该协议要求属性:bounds、center、transform。如果对象是自定义的,请手动实现该协议。
UIKit Dynamics包含七种内置行为类型,每种模拟特定的物理效果。行为可以组合:例如,重力+碰撞+弹性可以产生物体下落和弹跳的效果。
| Behavior | 物理效果 | 关键参数 |
|---|---|---|
| UIGravityBehavior | 重力(力矢量) | gravityDirection (dx, dy), magnitude |
| UICollisionBehavior | 对象和边界的碰撞 | collisionMode, translucence, collisionDelegate |
| UIAttachmentBehavior | 连接(弹簧/缆绳) | anchorPoint, length, damping, frequency |
| UISnapBehavior | 带阻尼吸引到点 | snapPoint, damping (0-1) |
| UIPushBehavior | 推/冲量(连续或瞬时) | pushDirection, magnitude, mode |
| UIDynamicItemBehavior | 对象的物理属性 | elasticity, friction, density, resistance |
| UIFieldBehavior | 力场(漩涡、磁铁、半径) | position, strength, falloff, region |
要实现对象的真实物理效果,需要同时使用多种行为。例如,对于下落卡牌:UIGravityBehavior(重力)、UICollisionBehavior(与底部碰撞)、UIDynamicItemBehavior(elasticity: 0.4用于弹跳)。所有行为都添加到同一个UIDynamicAnimator。
用于UIKit Dynamics的Swift代码创建UIDynamicAnimator并添加行为。下面的示例演示了具有多个对象、碰撞和自定义物理属性的重力效果。
import UIKit
class PhysicsViewController: UIViewController {
var animator: UIDynamicAnimator!
override func viewDidLoad() {
super.viewDidLoad()
// 1. 使用reference view创建动画器
animator = UIDynamicAnimator(referenceView: view)
// 2. 创建下落对象
let redView = UIView(frame: CGRect(x: 100, y: 50, width: 60, height: 60))
redView.backgroundColor = .systemRed
redView.layer.cornerRadius = 8
view.addSubview(redView)
let blueView = UIView(frame: CGRect(x: 200, y: 30, width: 60, height: 60))
blueView.backgroundColor = .systemBlue
blueView.layer.cornerRadius = 8
view.addSubview(blueView)
// 3. 重力
let gravity = UIGravityBehavior(items: [redView, blueView])
gravity.gravityDirection = CGVector(dx: 0, dy: 1)
gravity.magnitude = 0.8
animator.addBehavior(gravity)
// 4. 碰撞
let collision = UICollisionBehavior(items: [redView, blueView])
collision.translatesReferenceBoundsIntoBoundary = true
collision.collisionDelegate = self
animator.addBehavior(collision)
// 5. 物理属性
let itemBehavior = UIDynamicItemBehavior(items: [redView, blueView])
itemBehavior.elasticity = 0.6
itemBehavior.friction = 0.3
itemBehavior.density = 1.0
animator.addBehavior(itemBehavior)
// 6. 点击时的Snap行为
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
view.addGestureRecognizer(tap)
}
@objc
func handleTap(_ gesture: UITapGestureRecognizer) {
let point = gesture.location(in: view)
// 每个对象的Snap行为
for subview in view.subviews where subview is UIView {
animator.removeAllBehaviors()
let snap = UISnapBehavior(item: subview, snapTo: point)
snap.damping = 0.5
animator.addBehavior(snap)
}
}
}
extension PhysicsViewController: UICollisionBehaviorDelegate {
func collisionBehavior(_ behavior: UICollisionBehavior,
beganContactFor item1: UIDynamicItem,
with item2: UIDynamicItem,
at point: CGPoint) {
print("碰撞位置:(point)")
}
}该示例创建了两个具有重力、碰撞和物理属性的View。UISnapBehavior在触摸时将对象以0.5的阻尼吸引到触摸点。UICollisionBehaviorDelegate跟踪碰撞的开始以产生声音和视觉效果。
UIKit Dynamics在iOS应用程序中用于非游戏界面物理效果。常见场景:卡堆动画(UISnapBehavior用于吸引到中心)、错误时屏幕抖动(UIDynamicItemBehavior + UIPushBehavior)、引导页中的「流星」效果(gravity + collision + field behavior)。
为获得最佳性能,UIKit Dynamics建议不超过15-20个同时具有物理效果的对象。每个对象需要在每一帧计算碰撞和力。超过限制时,使用CALayer代替UIView — Core Animation处理图层更高效。
常见问题
UIKit Dynamics基于刚体模拟建模物理交互(重力、碰撞、力)。Core Animation — 根据给定的时间曲线动画CALayer属性(position、opacity、transform)。Dynamics适用于交互式物理,Core Animation适用于预定义的过渡。
SwiftUI中没有对UIKit Dynamics的直接支持。对于物理效果,请在UIViewRepresentable中使用UIKitView + UIDynamicAnimator,或通过SwiftUI中的Animation.spring()和withAnimation()使用自定义动画。
调用animator.removeAllBehaviors() — 这将移除所有活跃的物理行为。当所有对象达到静止状态时,animator会自动暂停。对于临时暂停,使用animator.isPaused属性或移除单个行为。
Apple建议不超过15-20个对象具有完整物理效果(重力+碰撞+力)。对于大规模动画,使用UIFieldBehavior — 它集中计算力而无需成对碰撞,从而减少CPU负载。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。