基于UICollisionBehavior与UIPanGesture的拼图应用碰撞检测问题
解决UIKit Dynamics在拼图应用中的异常问题
1. 移除重力与弹跳效果
- 不要向
UIDynamicAnimator添加UIGravityBehavior,若已添加则调用animator.removeBehavior(gravityBehavior)移除。 - 若使用
UICollisionBehavior,设置restitution = 0消除弹跳,且按需关闭屏幕边界碰撞:let collisionBehavior = UICollisionBehavior(items: puzzlePieces) collisionBehavior.restitution = 0 // 不需要屏幕边界碰撞则注释此行 // collisionBehavior.translatesReferenceBoundsIntoBoundary = true animator.addBehavior(collisionBehavior) - 避免添加
UIBounceBehavior,该行为会主动生成弹跳效果。
2. 禁止拖拽时的旋转
- 给所有拼图块添加
UIDynamicItemBehavior并禁用旋转:let itemBehavior = UIDynamicItemBehavior(items: puzzlePieces) itemBehavior.allowsRotation = false animator.addBehavior(itemBehavior) - 或让拼图块的自定义视图类实现
UIDynamicItem协议,重写属性:class PuzzlePieceView: UIView, UIDynamicItem { var allowsRotation: Bool { return false } // 其他UIDynamicItem协议实现(如需要) }
3. 修复吸附时的拼图变形问题
直接修改frame后调用updateItem(usingCurrentState:)会与UIKit Dynamics的状态管理冲突,推荐以下两种方案:
- 方案1:用
UISnapBehavior平滑吸附// 移除该拼图块的所有现有行为 animator.behaviors.filter { $0.items.contains(actualPuzzle) }.forEach { animator.removeBehavior($0) } // 创建吸附行为,目标为snapshotView的中心 let snapBehavior = UISnapBehavior(item: actualPuzzle, snapTo: snapshotView.center) snapBehavior.damping = 1.0 // 无阻尼,直接到位 animator.addBehavior(snapBehavior) actualPuzzle.isHidden = false // 吸附完成后可移除该行为 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { animator.removeBehavior(snapBehavior) } - 方案2:先移除再重新添加到动画器
animator.removeItem(actualPuzzle) actualPuzzle.frame = snapshotView.frame actualPuzzle.isHidden = false animator.addItem(actualPuzzle) // 重新添加之前的动态行为(如itemBehavior、collisionBehavior)
4. 基于UIBezierPath检测邻接碰撞,允许重叠且无物理交互
要实现仅检测碰撞、不产生物理阻挡的效果,按以下步骤操作:
- 配置拼图块的碰撞路径
让拼图块的自定义视图返回自定义碰撞路径:class PuzzlePieceView: UIView, UIDynamicItem { var collisionBoundingPath: UIBezierPath { // 返回你定义的四边UIBezierPath return yourCustomBezierPath } var collisionBoundsType: UIDynamicItemCollisionBoundsType { return .path } var allowsRotation: Bool { return false } } - 配置碰撞行为仅用于检测
创建UICollisionBehavior并设置代理,禁用物理碰撞响应:let collisionBehavior = UICollisionBehavior(items: puzzlePieces) collisionBehavior.collisionDelegate = self collisionBehavior.collisionMode = .custom animator.addBehavior(collisionBehavior) - 监听碰撞事件并触发吸附
实现UICollisionBehaviorDelegate,仅处理碰撞检测、阻止物理交互:extension YourViewController: UICollisionBehaviorDelegate { func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item1: UIDynamicItem, with item2: UIDynamicItem, at p: CGPoint) { guard let piece1 = item1 as? PuzzlePieceView, let piece2 = item2 as? PuzzlePieceView else { return } // 根据你的拼图逻辑判断是否为邻接块 if arePiecesAdjacent(piece1, piece2) { // 触发吸附逻辑,比如调用之前的snapBehavior方法 snapPieceToAdjacentPosition(piece1, target: piece2) } } // 阻止物理碰撞的实际作用,仅保留检测事件 func collisionBehavior(_ behavior: UICollisionBehavior, shouldBeginContactFor item1: UIDynamicItem, with item2: UIDynamicItem, at p: CGPoint) -> Bool { return false } }
内容的提问来源于stack exchange,提问作者Piotr Gawłowski
相关产品推荐
相关产品推荐

