You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何为视图帧变化添加观察者及检测视图碰撞

嘿,针对你的问题,我有两种实用的解决方案,刚好对应你需求的不同场景:

方案一:通过KVO观察视图帧变化(通用场景)

如果只是单纯需要监听视图的frame或bounds变化,KVO(键值观察)是最直接的方式,步骤如下:

  1. 注册观察者
    在视图控制器的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)
    }
    
  2. 实现观察回调
    重写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")
        }
    }
    
  3. 移除观察者(重要!)
    记得在视图控制器销毁时移除观察者,避免内存泄漏:

    deinit {
        carView.removeObserver(self, forKeyPath: #keyPath(UIView.frame))
        // 若注册了bounds观察者,也要对应移除
        // carView.removeObserver(self, forKeyPath: #keyPath(UIView.bounds))
    }
    

⚠️ 注意:如果视图使用AutoLayout布局,frame可能会频繁更新,所以碰撞检测逻辑尽量轻量化,避免影响性能。

方案二:利用UIKit Dynamics原生碰撞检测(重力场景专属)

既然你已经在使用UIGravityBehavior,其实UIKit Dynamics自带了成熟的碰撞检测机制,比手动监听帧变化更准确高效,推荐优先使用:

  1. 创建并配置碰撞行为
    在初始化重力行为的同时,添加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)
    
  2. 实现碰撞代理方法
    扩展你的视图控制器,实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:26:33