03-主题|事件响应者链@iOS-响应者链与nextResponder详解

本文专门讲解 iOS 响应者链 的构成与传递:nextResponder(API 中为 next)的规则、链的路径示意、事件沿链传递与转发方式,以及如何通过重写 next 修改链。与「事件如何找到目标」配合阅读可参见 02-hitTest 与事件传递详解


一、响应者链是什么

响应者链 是由 UIRespondernext (文档与习惯上常称 nextResponder)串联起来的一条链。当第一响应者不处理某事件时,系统会把该事件传给 next,再传给 next.next,直到某个响应者处理或链结束(next == nil[1]

触摸事件 而言,第一响应者通常是 hit-test 得到的视图;对按键、摇动、编辑菜单等,第一响应者多为当前获得焦点的对象(如 UITextField)。


二、nextResponder 的构成规则

各子类对 next 的实现决定了链的走向。以下为 UIKit 中的典型规则 [[1]][2]

当前响应者 next 通常为
UIView 若该 view 是某个 UIViewController 的根 view (viewController.view),则 next 为该 UIViewController ;否则为 superview
UIViewController 若该 VC 的 view 是某 window 的根 view,则 next 为 UIWindow ;若该 VC 被其他 VC present,则 next 为 presenting ViewController
UIWindow UIApplication
UIApplication App Delegate(仅当 delegate 是 UIResponder 子类且不是 view、viewController 或 app 本身时)
链尾 nil

因此,典型链路径为:

text 复制代码
Hit-test View(或 First Responder)
  → 其 UIViewController(若存在)
  → 该 VC 的 view 的 superview
  → ... 逐级 superview ...
  → Window 的 rootViewController.view
  → rootViewController
  → UIWindow
  → UIApplication
  → App Delegate(若符合条件)
  → nil

三、响应者链路径示意

flowchart TB subgraph 视图层级 V[Hit-test View] P[Superview] R[Root View] end subgraph 链路径 V --> VC[UIViewController] VC --> P P --> R R --> VC2[Root VC] VC2 --> W[UIWindow] W --> App[UIApplication] App --> Del[App Delegate] Del --> nil[nil] end

简化理解:从被点中的 view 开始,先到管理它的 ViewController (若有),再到该 view 的 superview ,一路到 window → application → app delegate,最后到 nil。

3.1 响应者链知识结构(思维导图)

mindmap root((nextResponder 链)) 起点 hit-test view First Responder 规则 UIView → VC 或 superview UIViewController → superview 或 window UIWindow → UIApplication UIApplication → App Delegate / nil 传递 touchesBegan 等转发 target nil 沿链找 target 修改 重写 next 属性

3.2 泳道图:事件沿链传递的参与方

flowchart LR subgraph 视图层 V1[Hit-test View] V2[父 View] end subgraph 控制器层 VC[ViewController] end subgraph 窗口与应用 W[UIWindow] App[UIApplication] Del[App Delegate] end V1 -->|不处理则 next| VC VC -->|next| V2 V2 -->|next| W W --> App App --> Del

四、事件沿链传递与转发方式

4.1 触摸事件的传递

触摸事件(touchesBegan / touchesMoved / touchesEnded / touchesCancelled)会先发给 hit-test 得到的视图。若该视图未重写 这些方法,UIKit 的默认实现会把事件传给 next,即沿链向上传递 [[2]]。

若视图重写 了触摸方法但希望「自己处理一部分,其余交给链上」:需显式调用 next 的对应方法,否则事件不会继续传递。

swift 复制代码
// 示例:将触摸事件交给下一响应者
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    // 可选:在此做自己的逻辑(如埋点、高亮)
    next?.touchesBegan(touches, with: event)
}

不调用 next?.touchesBegan(...) 则链在此中断,上层视图或 ViewController 不会收到该触摸。

商用场景示例 :自定义 Cell 内的某个 view 只做展示,希望点击「透传」到 Cell 或 VC 做统一处理(如整行点击跳详情);在自定义 view 中重写 touchesEnded 并调用 next?.touchesEnded(touches, with: event),由上层决定是否跳转。

延伸 :除系统 next 链外,业务层还常用 DelegateBlock/闭包函数封装快速枚举/for 循环 等方式做事件或回调的传递,与「链」形成互补。详见 06-响应者链传递方式与编程模式详解

4.2 Action 与 target-nil

UIControl 的 target-action 中,若 target 为 nil ,系统会从当前第一响应者开始,沿 next 查找第一个实现了对应 action 的响应者并调用,即 action 沿响应者链查找 target [[1]]。编辑菜单(复制/粘贴等)也利用该机制在链上查找能执行 copy(_:)paste(_:) 等的对象。


五、修改响应者链:重写 next

可通过重写 next 属性改变链的走向,使事件或 action 先经过指定对象 [[1]]。

swift 复制代码
// 示例:让某自定义 view 的 next 指向指定的 responder(如父 VC)
override var next: UIResponder? {
    return parentViewController ?? super.next
}

这样,当该 view 不处理事件时,事件会先传给 parentViewController,再按 parentViewController.next 继续。适用于希望「某视图的上级一定是某个 VC」的场景。

商用场景示例 :列表页中某个自定义 view(如卡片内嵌的容器)希望点击后一定由当前页的 ViewController 处理(如弹窗、路由),而不经过中间若干 superview;通过重写 next 指向 VC,可保证 Action 或 touches 先到达 VC。

遍历响应者链的调试/工具代码(Swift):

swift 复制代码
extension UIResponder {
    /// 打印从当前节点到链尾的整条响应者链,便于调试
    func printResponderChain() {
        var current: UIResponder? = self
        var level = 0
        while let r = current {
            print(String(repeating: "  ", count: level) + "\(type(of: r))")
            current = r.next
            level += 1
        }
    }
}
// 使用:在某个 view 或 VC 中调用 self.printResponderChain()

六、与 hit-test 的关系小结

阶段 方向 作用
Hit-Testing 自顶向下 确定「谁被点中」→ 该 view 为触摸的 first responder
Responder Chain 自底向上 从该 view 开始,沿 next 传递,直到有人处理或链结束

两者配合:hit-test 决定链的起点 ,next 决定链的走向与传递顺序 。详见 01-总纲02-hitTest 与事件传递。业务中「谁来处理」除依赖链外,还可通过 06-响应者链传递方式与编程模式详解 中的 Delegate、Block、函数封装、遍历传递等模式实现。


参考文献

1\] [Using responders and the responder chain to handle events - Alter the responder chain](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.apple.com%2Fdocumentation%2Fuikit%2Fusing-responders-and-the-responder-chain-to-handle-events "https://developer.apple.com/documentation/uikit/using-responders-and-the-responder-chain-to-handle-events") \[2\] [UIResponder - Managing the responder chain (next)](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.apple.com%2Fdocumentation%2Fuikit%2Fuiresponder "https://developer.apple.com/documentation/uikit/uiresponder")

相关推荐
三小河2 小时前
VS Code 集成 claude-code 教程:告别海外限制,无缝对接国内大模型
前端·程序员
用户60572374873082 小时前
AI 编码助手的规范驱动开发 - OpenSpec 初探
前端·后端·程序员
没有故事的Zhang同学2 小时前
02-主题|事件响应者链@iOS-hitTest与事件传递详解
程序员
没有故事的Zhang同学5 小时前
03-超级App软件平台@路由规则设计-【Universal-Links】与【App-Links详解】
程序员
没有故事的Zhang同学6 小时前
01-超级App软件平台@路由规则设计-【总纲】
程序员
没有故事的Zhang同学6 小时前
04-超级App软件平台@路由规则设计-【组件化路由框架详解】
程序员
没有故事的Zhang同学6 小时前
07-超级App软件平台@路由规则设计-【通用路由管理组件设计】
程序员
没有故事的Zhang同学10 小时前
02-Debug调试@网络-Wireshark网络抓包工具:从原理到实践
程序员