Swift中如何为视图帧变化添加观察者及检测视图碰撞
嘿,针对你的问题,我有两种实用的解决方案,刚好对应你需求的不同场景:
方案一:通过KVO观察视图帧变化(通用场景)
如果只是单纯需要监听视图的frame或bounds变化,KVO(键值观察)是最直接的方式,步骤如下:
注册观察者
在视图控制器的viewDidLoad(或其他合适的初始化时机)中,为目标视图注册帧变化的观察者:override func viewDidLoad() { super.viewDidLoad() // 注册carView的frame变化观察者 carView.addObserver(self, forKeyPath: #keyPath(UIView.frame), options: [.new, .old], context: nil) // 若需要监听bounds变化,也可以添加下面这行 // carView.addObserver(self, forKeyPath: #keyPath(UIView.bounds), options: [.new, .old], context: nil) }实现观察回调
重写observeValue方法,在帧变化时执行你的碰撞检测逻辑:override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { // 确认是carView的frame发生了变化 if keyPath == #keyPath(UIView.frame), object as? UIView == carView { // 执行碰撞检测 checkCollision() } } // 封装你的碰撞检测逻辑 private func checkCollision() { if carView.frame.intersects(carImageView.frame) { print("it collides") } }移除观察者(重要!)
记得在视图控制器销毁时移除观察者,避免内存泄漏:deinit { carView.removeObserver(self, forKeyPath: #keyPath(UIView.frame)) // 若注册了bounds观察者,也要对应移除 // carView.removeObserver(self, forKeyPath: #keyPath(UIView.bounds)) }
⚠️ 注意:如果视图使用AutoLayout布局,frame可能会频繁更新,所以碰撞检测逻辑尽量轻量化,避免影响性能。
方案二:利用UIKit Dynamics原生碰撞检测(重力场景专属)
既然你已经在使用UIGravityBehavior,其实UIKit Dynamics自带了成熟的碰撞检测机制,比手动监听帧变化更准确高效,推荐优先使用:
创建并配置碰撞行为
在初始化重力行为的同时,添加UICollisionBehavior并设置碰撞代理:// 先初始化动态动画器(假设你还没创建) let animator = UIDynamicAnimator(referenceView: self.view) // 配置重力行为 let gravityBehavior = UIGravityBehavior(items: [carView]) animator.addBehavior(gravityBehavior) // 创建碰撞行为,添加需要检测碰撞的两个视图 let collisionBehavior = UICollisionBehavior(items: [carView, carImageView]) // 设置碰撞模式为检测所有视图间的碰撞(包括边界,若不需要可改为.items) collisionBehavior.collisionMode = .everything // 设置代理以监听碰撞事件 collisionBehavior.collisionDelegate = self animator.addBehavior(collisionBehavior)实现碰撞代理方法
扩展你的视图控制器,实现UICollisionBehaviorDelegate协议,在碰撞发生时触发逻辑:extension YourViewController: UICollisionBehaviorDelegate { func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item1: UIDynamicItem, with item2: UIDynamicItem, at p: CGPoint) { // 判断碰撞的两个视图是否是我们关心的carView和carImageView let isTargetCollision = (item1 as? UIView == carView && item2 as? UIView == carImageView) || (item1 as? UIView == carImageView && item2 as? UIView == carView) if isTargetCollision { print("it collides") } } // 可选:监听碰撞结束事件 func collisionBehavior(_ behavior: UICollisionBehavior, endedContactFor item1: UIDynamicItem, with item2: UIDynamicItem) { // 这里可以处理碰撞结束后的逻辑 } }
这个方案的优势在于:UIKit Dynamics会自动处理物理运动中的帧同步,碰撞检测更精准,而且不需要你手动处理频繁的帧更新监听,性能更优。
内容的提问来源于stack exchange,提问作者Farbodc137
相关产品推荐
相关产品推荐

