如何让可拖拽带重力效果的UIView触碰屏幕底部时改变颜色
实现方块触碰屏幕底部时变色的功能
我正在开发一款简单游戏,初始状态下蓝色方块会在几秒后下落,用户可拖拽并释放它,方块会再次下落。我希望实现一个功能:当蓝色方块触碰屏幕底部边缘时改变其颜色。以下是我的代码:
import UIKit import CoreMotion class CanvasViewController: UIViewController { @IBOutlet weak var blueSquare: UIView! var animator: UIDynamicAnimator! var gravity: UIGravityBehavior! var motion: CMMotionManager! var queue: OperationQueue! // used for updating UI objects with motion var panGesture = UIPanGestureRecognizer() let colors = [UIColor.blue, UIColor.red, UIColor.yellow, UIColor.green] override func viewDidLoad() { super.viewDidLoad() queue = OperationQueue.current animator = UIDynamicAnimator(referenceView: self.view) gravity = UIGravityBehavior(items: [blueSquare]) motion = CMMotionManager() panGesture = UIPanGestureRecognizer(target: self, action: #selector(CanvasViewController.draggedView(_:))) blueSquare.isUserInteractionEnabled = true blueSquare.addGestureRecognizer(panGesture) animator.addBehavior(gravity) // the objects that responds to collision let collision = UICollisionBehavior(items: [blueSquare]) // the boundary AKA the borders. In this case if the full ViewController view collision.addBoundary(withIdentifier: "borders" as NSCopying, for: UIBezierPath(rect: self.view.frame)) animator.addBehavior(collision) //Elasticity let bounce = UIDynamicItemBehavior(items: [blueSquare]) bounce.elasticity = 8 } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.tabBarController?.tabBar.isHidden = true } @objc func draggedView(_ sender:UIPanGestureRecognizer){ self.view.bringSubviewToFront(blueSquare) let translation = sender.translation(in: self.view) blueSquare.center = CGPoint(x: blueSquare.center.x + translation.x, y: blueSquare.center.y + translation.y) sender.setTranslation(CGPoint.zero, in: self.view) animator.updateItem(usingCurrentState: blueSquare) } }
解决方法
1. 监听碰撞事件
要实现触碰底部变色,需要借助UICollisionBehaviorDelegate来监听方块和边界的碰撞。首先让控制器遵循这个协议:
class CanvasViewController: UIViewController, UICollisionBehaviorDelegate { // 原有代码不变 }
2. 调整碰撞行为的定义
把原来的局部collision变量改成类属性,方便在代理方法中调用,同时设置代理:
// 新增类属性 var collision: UICollisionBehavior! override func viewDidLoad() { super.viewDidLoad() // ... 原有代码 // 初始化碰撞行为并设置代理 collision = UICollisionBehavior(items: [blueSquare]) collision.addBoundary(withIdentifier: "borders" as NSCopying, for: UIBezierPath(rect: self.view.frame)) collision.collisionDelegate = self animator.addBehavior(collision) // ... 原有代码 }
3. 实现碰撞检测逻辑
添加代理方法,判断碰撞是否发生在屏幕底部,然后切换方块颜色:
func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) { guard identifier as? String == "borders", let square = item as? UIView else { return } // 通过方块底部位置判断是否触碰底部 let squareBottomY = square.frame.maxY let screenBottomY = view.bounds.maxY if squareBottomY >= screenBottomY - 1 { // 留1pt误差避免精度问题 // 从颜色数组中选一个和当前不同的颜色 let currentColor = square.backgroundColor var newColor: UIColor repeat { newColor = colors.randomElement()! } while newColor == currentColor square.backgroundColor = newColor } }
4. 修复弹性值问题
注意你代码中bounce.elasticity = 8是错误的,弹性值范围是0到1(0完全不弹,1完全弹性),同时要把弹性行为添加到动画器中:
let bounce = UIDynamicItemBehavior(items: [blueSquare]) bounce.elasticity = 0.7 // 设置合理的弹性值 animator.addBehavior(bounce) // 别忘了添加到动画器
完整修改后的代码
import UIKit import CoreMotion class CanvasViewController: UIViewController, UICollisionBehaviorDelegate { @IBOutlet weak var blueSquare: UIView! var animator: UIDynamicAnimator! var gravity: UIGravityBehavior! var motion: CMMotionManager! var collision: UICollisionBehavior! var queue: OperationQueue! var panGesture = UIPanGestureRecognizer() let colors = [UIColor.blue, UIColor.red, UIColor.yellow, UIColor.green] override func viewDidLoad() { super.viewDidLoad() queue = OperationQueue.current animator = UIDynamicAnimator(referenceView: self.view) gravity = UIGravityBehavior(items: [blueSquare]) motion = CMMotionManager() panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:))) blueSquare.isUserInteractionEnabled = true blueSquare.addGestureRecognizer(panGesture) animator.addBehavior(gravity) // 碰撞行为初始化与代理设置 collision = UICollisionBehavior(items: [blueSquare]) collision.addBoundary(withIdentifier: "borders" as NSCopying, for: UIBezierPath(rect: self.view.frame)) collision.collisionDelegate = self animator.addBehavior(collision) // 修复弹性值并添加到动画器 let bounce = UIDynamicItemBehavior(items: [blueSquare]) bounce.elasticity = 0.7 animator.addBehavior(bounce) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.tabBarController?.tabBar.isHidden = true } @objc func draggedView(_ sender:UIPanGestureRecognizer){ self.view.bringSubviewToFront(blueSquare) let translation = sender.translation(in: self.view) blueSquare.center = CGPoint(x: blueSquare.center.x + translation.x, y: blueSquare.center.y + translation.y) sender.setTranslation(CGPoint.zero, in: self.view) animator.updateItem(usingCurrentState: blueSquare) } // 碰撞检测与变色逻辑 func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) { guard identifier as? String == "borders", let square = item as? UIView else { return } let squareBottomY = square.frame.maxY let screenBottomY = view.bounds.maxY if squareBottomY >= screenBottomY - 1 { let currentColor = square.backgroundColor var newColor: UIColor repeat { newColor = colors.randomElement()! } while newColor == currentColor square.backgroundColor = newColor } } }
内容的提问来源于stack exchange,提问作者Mateo Valencia Minota
相关产品推荐
相关产品推荐

