使用 Swift 开发 iOS 应用时,很多人都曾疑惑过:明明点击了按钮,为什么会是这个视图控制器收到事件?
深入追踪触摸事件在屏幕中如何流动,就会发现底层使用了责任链模式。
先说结论,UIResponder 链是 Apple 在框架层实现责任链模式(Chain of Responsibility)的典型案例,而责任链模式是 GoF 设计模式之一。responder 会将责任传递给下一个 responder,直到找到能够处理事件的对象。
本文会通过拆解代码,整理责任链模式的概念、UIResponder 链的实际工作方式,以及了解这些内容对实际开发的帮助。
先看核心摘要
- 责任链模式是一种设计模式,多个对象像链条一样传递请求,直到找到能够处理请求的对象。
- iOS 的 UIResponder 链就是在框架层实现了这一模式。
- 触摸事件会依次从 UIView 传递到上层视图、视图控制器、窗口和应用程序。
- next 属性指向链中的下一个环节;如果没有对象处理事件,事件就会直接被丢弃。
什么是责任链模式?
责任链模式是一种行为型模式,用于解耦请求发送方与处理方。
简单来说,它就像公司里的审批流程。助理经理能处理就到此为止,不能处理就交给经理,经理也无法处理时再交给总监。
发起请求的人不需要知道最终由谁审批,只要把请求交给链上的第一个环节,后续就会自动传递。
这个模式的核心是,每个处理对象只需要知道两件事:自己能否处理请求,以及不能处理时下一个对象是谁。
用 Swift 做一个非常简单的版本,大致结构如下。
class Handler {
var next: Handler? // 链中的下一个环节
func handle(_ request: Int) {
// 无法处理时将责任传递给下一个对象
next?.handle(request)
}
}
通过一个 next 属性连接链条,无法处理时交给 next,这种结构原样内置在 UIResponder 中。
UIResponder 链是如何工作的?
在 iOS 中,所有能够接收触摸、动作、远程控制等事件的对象都会继承 UIResponder。UIView、UIViewController、UIWindow 和 UIApplication 都是 UIResponder 的子类。
因此,这些对象各自都有一个属性,其准确名称就是 next。它定义在 UIResponder 中。
// UIResponder中定义的属性
var next: UIResponder? { get }
用户触摸屏幕时,系统首先找到最内层、实际被触摸的视图。这称为命中测试,同时会确定 first responder 候选对象。
如果该视图没有处理事件,系统就沿着 next 向上查找。顺序大致如下。
- UIView(被触摸的视图)
- 上层的各个超视图
- 管理该视图的 UIViewController
- UIWindow
- UIApplication
- UIApplicationDelegate
即使遍历完整条链也没有对象处理事件,事件也只会被静默丢弃。应用不会崩溃或报错,只是忽略这个事件。
有趣的是,视图的 next 并不总是超视图。
如果该视图是视图控制器的根视图,next 就是视图控制器,而不是超视图。
了解这一点对实际开发有什么好处?
说实话,刚开始即使不了解这种内部结构,应用也能正常运行。但理解之后,调试方式会明显不同。
分享一个我遇到的案例:自定义视图中的点击手势没有生效。
按顺序追踪事件在哪个 responder 处停止后,我很快发现了原因:上层视图的 isUserInteractionEnabled 为 false,责任链在那里断开了。
自定义事件传播也很有用。你可以定义沿着 UIResponder 链向上传递的消息,让深层视图产生的操作无需 delegate,就能传递到上层视图控制器。
收起键盘时常用的 endEditing(true) 其实也利用了这条链。它会找到 first responder,并调用 resignFirstResponder。
总之,理解 UIResponder 链后,遇到事件无响应时,就能有逻辑地追踪中断位置,也能避免滥用 delegate,设计出向上传递事件的方案。
常见问题
Q. first responder 和 responder 链是不同的概念吗? A. first responder 是有资格优先接收事件的一个对象,而 responder 链是从该 first responder 向上延伸的整条链。
Q. next 总是指向超视图吗? A. 不是。普通视图指向超视图,但视图控制器的根视图会将视图控制器作为 next。
责任链模式这个名字可能听起来很难,但归根结底只有一个简单原则:无法处理时就交给下一个对象。希望本文能帮助你理解 UIResponder 链这个 iOS 隐藏的骨架。下次遇到事件无响应的 Bug 时,可以沿着这条链逐个环节追踪。

