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 事件传递和响应机制 ,掌握这个知识点有助于 处理自定义手势、拦截触摸事件、调整事件传递逻辑!🚀

相关推荐
萌萌哒草头将军41 分钟前
🚀🚀🚀尤雨溪:Vite 和 JavaScript 工具的未来
前端·vue.js·vuex
Fly-ping1 小时前
【前端】cookie和web stroage(localStorage,sessionStorage)的使用方法及区别
前端
我家媳妇儿萌哒哒1 小时前
el-upload 点击上传按钮前先判断条件满足再弹选择文件框
前端·javascript·vue.js
天天向上10241 小时前
el-tree按照用户勾选的顺序记录节点
前端·javascript·vue.js
sha虫剂1 小时前
如何用div手写一个富文本编辑器(contenteditable=“true“)
前端·vue.js·typescript
咔咔库奇2 小时前
深入探索 Vue 3 Fragments:从原理到实战的全方位指南
前端·javascript·vue.js
要加油哦~2 小时前
vue | vue 插件化机制,全局注册 和 局部注册
前端·javascript·vue.js
猫头虎-前端技术2 小时前
HTML 与 CSS 的布局机制(盒模型、盒子定位、浮动、Flexbox、Grid)问题总结大全
前端·javascript·css·vue.js·react.js·前端框架·html
Skrrapper2 小时前
【三大前端语言之一】静态网页语言:HTML详解
前端·html