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

相关推荐
超哥--6 小时前
B站视频内容智能分析系统(九):React 前端与管理面板
前端·react.js·前端框架
Cutecat_9 小时前
视频字幕处理工具横向:提取模式 vs 编辑模式,该如何选择
android·前端·ios·语音识别
qq_422152579 小时前
PDF 加水印工具怎么选?2026 年文档版权保护方案对比
前端·pdf·github
kyriewen10 小时前
手写 Promise.all、race、any:不到 30 行代码,解决并发异步的所有姿势
前端·javascript·面试
brucelee18610 小时前
OpenClaw 浏览器控制(Chrome MCP)完整教程
前端·chrome
ct97811 小时前
React 状态管理方案深度对比
开发语言·前端·react
胡志辉的博客11 小时前
深入浅出理解浏览器事件循环:从一道输出题讲到 Chrome 源码
前端·javascript·chrome·chromium·event loop
代码不加糖11 小时前
js中不会冒泡的事件有哪些?
前端·javascript·vue.js
懂懂tty11 小时前
Vue2与Vue3之间API差异
前端·javascript·vue.js
AI焦点11 小时前
跨越协议鸿沟:Tool Use状态机从Anthropic到OpenAI兼容体系的适配要点
前端·人工智能