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

1 阅读4分钟

本文专门讲解 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
UIWindowUIApplication
UIApplicationApp Delegate(仅当 delegate 是 UIResponder 子类且不是 view、viewController 或 app 本身时)
链尾nil

因此,典型链路径为:

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 的对应方法,否则事件不会继续传递。

// 示例:将触摸事件交给下一响应者
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]]。

// 示例:让某自定义 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):

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
[2] UIResponder - Managing the responder chain (next)