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

相关推荐
面向星辰1 小时前
html各种常用标签
前端·javascript·html
梦6501 小时前
HTML新属性
前端
东风西巷3 小时前
PDFgear:免费全能的PDF处理工具
前端·pdf·软件需求
森之鸟4 小时前
Mac电脑上如何打印出字体图标
前端·javascript·macos
mCell4 小时前
GSAP 入门指南
前端·javascript·动效
gnip5 小时前
组件循环引用依赖问题处理
前端·javascript
Aotman_6 小时前
el-input textarea 禁止输入中文字符,@input特殊字符实时替换,光标位置保持不变
前端·javascript·vue.js·前端框架·es6
Nan_Shu_6146 小时前
Web前端面试题(1)
前端·面试·职场和发展
lypzcgf6 小时前
Coze源码分析-资源库-创建知识库-前端源码-核心组件
前端·typescript·react·coze·coze源码分析·ai应用平台·agent开发平台
百思可瑞教育7 小时前
在Vue项目中Axios发起请求时的小知识
前端·javascript·vue.js·北京百思教育