iOS 事件响应链 & 事件传递链

iOS 事件响应链 & 事件传递链

1. 事件传递链(Event Delivery Chain)

概念

当用户触摸屏幕时,事件首先由 系统(UIApplication) 接收,然后按以下顺序传递:

  1. UIApplication → 2. UIWindow → 3. 根视图控制器(Root ViewController) → 4. 视图层级(UIView)

事件传递流程

当手指触摸屏幕时:

  1. UIApplication 接收到事件,将其交给当前的 keyWindow(UIWindow) 处理。
  2. UIWindow 调用 hitTest(_:with:) 方法,找到触摸点所在的 最底层视图(最精确的 View)
  3. UIView 层级递归执行 hitTest(_:with:),最终返回触摸点所在的 最前面的子视图(即最符合触摸点的视图)。
  4. 如果 找到合适的 View ,事件将传递给这个 View 的 touchesBegan(_:with:) 方法,否则事件被丢弃。

事件传递代码示例

swift 复制代码
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    // 检查自己是否能接收事件
    if !self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01 {
        return nil
    }
    
    // 检查触摸点是否在当前 View 内
    if self.point(inside: point, with: event) {
        // 倒序遍历子视图,找到最前面的 View
        for subview in self.subviews.reversed() {
            let convertedPoint = subview.convert(point, from: self)
            if let hitView = subview.hitTest(convertedPoint, with: event) {
                return hitView
            }
        }
        return self // 如果没有找到子视图,则自己接收事件
    }
    return nil
}

2. 事件响应链(Responder Chain)

概念

事件传递链 找到目标 UIView 后,事件需要进行 响应处理 。如果该视图无法处理,事件会沿着 响应者链(Responder Chain) 向上传递,直到被某个响应者处理或最终被丢弃。

事件响应链传递路径

UIView 不能处理事件时,事件会沿着以下路径 向上传递

  1. 当前 View(UIView)
  2. View 的父视图(superview)
  3. View 所在的 ViewController(UIViewController)
  4. ViewController 所属的 UIWindow
  5. UIApplication(全局应用对象)
  6. 如果仍未处理,则被丢弃

事件响应链代码示例

swift 复制代码
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("\(self) 处理触摸事件")
    super.touchesBegan(touches, with: event) // 传递给下一个响应者
}

如果当前 View 没有实现 touchesBegan(_:with:) ,则会调用 nextResponder() 方法,将事件传递给 下一个响应者

如何获取 nextResponder()

swift 复制代码
override var next: UIResponder? {
    return self.superview // 下一个响应者是父视图
}

3. 事件传递链 vs 响应链

事件传递链(Event Delivery) 事件响应链(Responder Chain)
作用 负责找到 最适合的 View 处理事件 负责 事件处理,如果无法处理则向上传递
传递方向 UIApplication → UIWindow → 视图层级 当前 View 向上 → 父视图 → 控制器 → UIWindow → UIApplication
核心方法 hitTest(_:with:)pointInside(_:with:) touchesBegan(_:with:)nextResponder()
终点 找到 最前面的 View 找到 能够处理事件的响应者

4. 事件传递 & 响应示例

示例:自定义按钮 MyButton 传递 & 响应事件

swift 复制代码
class MyButton: UIButton {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        print("检查是否可以接收事件: \(self)")
        return super.hitTest(point, with: event)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("MyButton 处理触摸事件")
        super.touchesBegan(touches, with: event)
    }
}

如果 MyButton 无法处理事件 ,会沿着 响应链向上传递 ,直到 UIViewController 处理它。


5. 结论

  • 事件传递链 :确定由 哪个 View 处理事件hitTest(_:with:))。
  • 事件响应链 :确定 如何处理事件 ,如果当前 View 无法处理,则 沿着响应链传递nextResponder())。
  • 事件最终要么被处理,要么被丢弃

这就是 iOS 事件传递和响应机制 ,掌握这个知识点有助于 处理自定义手势、拦截触摸事件、调整事件传递逻辑!🚀

相关推荐
weixin_4435669817 分钟前
async/defer/preload性能优化
前端·css·html
王立志_LEO18 分钟前
React 18 和 Vue 3 生命周期钩子对比
前端
Riesenzahn21 分钟前
举例说明with属性的fill-available有什么应用场景?
前端·javascript
Riesenzahn21 分钟前
websocket和socket有什么区别?
前端·javascript
sakibcc26 分钟前
Cursor使用指南
前端·程序员·cursor
Monica9927 分钟前
BFCache和浏览器缓存
前端
Lecea_L33 分钟前
MockPilot2 Review
前端·next.js
了不起的大喜38 分钟前
HarmonyOS-订阅网络状态变化&RCP访问网络
前端
渴望成为python大神的前端小菜鸟39 分钟前
2025前端面试题(vue、react、uniapp、微信小程序、JS、CSS、其他)
前端·javascript·vue.js·面试·微信小程序·uni-app·react
晚风91439 分钟前
Vue 3中的Teleport:超越组件边界的渲染
前端·javascript·vue.js