最近给 CurrencyX 添加手势滑动返回遇到一些问题,特地复习了一下 Responder Chain 的内容,本文翻译并梳理了 Cocoa Event Handling Guide 中的 Responder Chain 部分。
定义
在 App 中发生的 Event 或者 Action Message 将会在 Responder Chain 中传递并得到处理,Responder Chain 指的是按顺序连接的一些 Responder Objects。由 First Responder 开始,对于接受到的 Message,Object 对其进行处理或者传递给 Next Responder。Responder 可以调用 delegate 方法进行事件处理。Application Kit 将自行创建 Responder Chain,我们也可以通过 setNextResponder:
方法来自定义 Responder Chain。
一个应用汇总可能有多个 Responder Chain,但在某一时刻只有一个 Responder Chain 是 Active 的。对于不同的 Event 或者 Action Message,Responder Chain 也是不同的。接下来将具体说明。
大部分 Event Message 将会传递给 Event 所发生窗口的 Responder Chain,NSWindow 根据事件类型不同来决定 Chain 的起点。一般情况下:
- Key Event(键盘事件)以 Window 的 First Responder 作为起点;
- Mouse or Tablet Event 以用户触发的 View 作为起点。
从起点开始,如果事件没有特殊处理,则会沿着 View Hierarchy 向上传递至 Window;一般来说,Window 的 First Responder 将是 Window 内当前“选中”的 View,Next Responder 则是 Superview。如果 Window 由 NSWindowController 来管理,Window Controller 将成为 Final Next Responder。在 NSView 之间,可以插入其他的 Responder。
如果 Final Responder 仍然没有对事件做出处理,将会调用 noResponderFor:
方法,对于键盘事件系统将发出 beep 提示音。Responder 可以重写该方法来提供额外的处理。
Application Kit 对于处理 Action Message 的 Responder Chain 构建比较复杂,根据以下两种情况作出了划分:
- Application 是否基于 Document Architecture,如果不是,那么它是否为 Window 提供了 NSWindowController;
- Application 当前的 Key Window 是否是它的 Main Window。
在应用运行时, Action Message 需要更灵活的 Runtime Mechanism 来决定它的 Target,因此构建 Responder Chain 的方法也更加精细。和 Event Message 一样,Action Message 不一定在单一的 Window 中得到处理。
Non-document-based
根据 Key Window 和 Main Window 是否为同一 Window;Window 是否由 Window Controller 管理分为几种情况讨论。
Key Window 即 Main Window
最简单的情况是,为一个单一窗口(Key Window 即 Main Window)的 Non-document-based Window 构建 Responder Chain:
- 以 Main Window 的 First Responder 为起点,按照 View Hierarchy 构建的 Responder Chain;
- Main Window 本身;
- Main Window 的 delegate(并不要求继承自 NSResponder);
- Application Object,即 NSApp;
- Application Object 的 Delegate (并不要求继承自 NSResponder)。
为单一窗口 Non-document-based 的 Application 处理 Action Message 构建的 Chain 如下图所示:
NSWindow 和 NSApplication 默认都将事件传递给 delegate 处理,而 delegate 并不属于 Responder Chain(Window 或 Application 的 nextResponder
并不返回它们的 delegate)。
Key Window 与 Main Window 不同
当 Application 的 Key Window 和 Main Window 并不是一个 Window 时,两个 Window 中的 Responder 共同组成了 Action Message 的 Responder Chain。首先,Key Window 的 Responder Chain 对 Action Message 作出相应,接着传递至 Main Window 的 Responder Chain:
- 以 Key Window 的 First Responder 为起点,按照 View Hierarchy 构建的 Responder Chain;
- Key Window 本身;
- Key Window 的 delegate(并不要求继承自 NSResponder);
- 以 Main Window 的 First Responder 为起点,按照 View Hierarchy 构建的 Responder Chain;
- Main Window 本身;
- Main Window 的 delegate(并不要求继承自 NSResponder);
- Application Object,即 NSApp;
- Application Object 的 Delegate (并不要求继承自 NSResponder)。
从第三阶段开始与以 Main Window 的 Responder 开始构建的 Responder Chain 相同。
Window 由 NSWindowController 管理
为由 Window Controller 管理的 Window 构建 Responder Chain 不同之处是在 Window 和 Window Delegate 之间插入了 Window Controller:
- 以 Main Window 的 First Responder 为起点,按照 View Hierarchy 构建的 Responder Chain;
- Main Window 本身;
- 管理 Main Window 的 NSWindowController(继承自 NSResponder);
- Main Window 的 delegate(并不要求继承自 NSResponder);
- Application Object,即 NSApp;
- Application Object 的 Delegate (并不要求继承自 NSResponder)。
为 Non-document-based 并由 NSWindowController 管理的 Application 处理 Action Message 构建的 Chain 如下图所示:
Document-based
对于 Document-based Application,默认为 Main Window 构建的 Responder Chain 在 Window Delegate 和 NSApp 间插入了 NSDocument:
- 以 Main Window 的 First Responder 为起点,按照 View Hierarchy 构建的 Responder Chain;
- Main Window 本身;
- 管理 Main Window 的 NSWindowController(继承自 NSResponder);
- Main Window 的 delegate(并不要求继承自 NSResponder);
- NSDocument Object(与 Window 的 delegate 不同);
- Application Object,即 NSApp;
- Application Object 的 Delegate (并不要求继承自 NSResponder)。
为 Document-based 的 Application 处理 Action Message 构建的 Chain 如下图所示:
在 Application Kit 中,除了处理 Event 和 Action 之外,Responder Chain 还有以下用途:
- 自动管理 Menu Item 和 Toolbar Item 的 enable 属性:当 Menu Item 的 Target 为 nil 时,会自动变为 Disable。为了判断 Target 是否为 nil,NSMenu 将根据 Menu Object 在不同的 Responder Chain 中进行搜索:
- 对于 Application Menu,NSMenu 将在 Full Responder Chain 搜索,如果存在 Object 实现了 Item 的 Action Method,则在
validateMenuItem:
返回YES
; - 对于 Context Menu,NSMenu 将仅在 Context Menu 相关 Window 的以相关 View 为起点的 Responder Chain 中搜索。
- 对于 Application Menu,NSMenu 将在 Full Responder Chain 搜索,如果存在 Object 实现了 Item 的 Action Method,则在
Toolbar Item 的 enable 搜索条件与 Menu Item 相同,结果将在 validateToolbarItem:
中返回。
详细参见:Enabling Menu Items 和 Validating Toolbar Items。
- Service 可用性:类似的,Services Facility 将在 Full Responder Chain 中传递
validRequestorForSendType:returnType:
消息,通过返回值判断其它 Application 提供的 Service 在当前 Application 中是否可用。
详细参见:Service Implementation Guide。
- 显示错误:Application Kit 将在与上述定义不同的 Responder Chain 中处理 Error 和 Error Presentation,通过 NSResponder 的
presentError:modalForWindow:delegate:didPresentSelector:contextInfo:
和presentError:
处理。
详细参见:Error Handling Programming Guide。
注释:
- Main Window:最前的 Document 或 Application Window;
- Key Window:当前活跃的 Window(Focus User Input);
- Application Menu:App 在系统 Bar 上的菜单栏;
- Context Menu:右击 View 时弹出的菜单栏。
支持我们
如果你觉得文章对你有帮助,可以买一个支持我们
关注我们公众号,获取最新文章推送