原文链接

以下,对官方文档作简单翻译和梳理

概述

App 通过响应者对象去接收和处理事件。响应者对象是 UIResponder 的实例,UIResponder 的子类包括了 UIView, UIViewController, UIApplication。响应者收到原生事件数据时必须处理该事件或继续向前传递给其他响应者对象。App 收到一个事件时,UIKit 会自动将该事件传递给最合适的响应者对象 [第一响应者]。

在一个活跃的响应链中,未处理的事件会在响应者间层层传递。

UIView …-> UIView -> UIViewController? -> UIWindow -> UIApplication -> UIApplicationDelegate

测定第一响应者

事件类型 第一响应者
Touch events 发生 Touch 的对象
Press events Focus 的对象
Shake-motion events 默认或自定义指派
Remote-control events 默认或自定义指派
Editing menu messages 默认或自定义指派

控件使用动作消息直接与其目标对象进行交流。当用户与控件发生交互时,该控件会发送一个动作消息到它的目标对象。动作消息不是事件,但它们利用了响应链。若控件没有实现动作方法,UIKit 将从其贯穿直到在响应链找到一个实现了动作方法的对象为止。

视图添加手势的话,手势会在视图之前会接收 Touch 和 Press 事件。如果手势无法识别一连串的触摸操作,UIKit 会将触摸操作传递给视图;如果视图为处理触摸操作,UIKit 将会把它们传入响应链。

Touch 事件归属判定

UIKit 使用 view-based 和 hit-testing 去决定触摸事件在哪里发生。进一步说,UIKit 在视图层级中比较了触摸位置和视图对象的 bounds。UIView 的 hitTest 方法会贯穿视图层级,找到最深的满足要求的子视图,这个子视图就是第一响应者。

如果触摸位置在视图 bounds 之外,hitTest 方法会忽略其所有的子视图。(针对 clipsToBounds=false 视图元素溢出 bounds 的场景)

触摸发生时,UIKit 会创建一个 UITouch 对象,UITouch 对象用一个 property 关联了对应的视图。触摸位置或其他参数改变时,UITouch 对象会更新信息,但关联的视图不会发生改变(即使触摸位置移动到视图外部)。触摸动作结束时,UIKit 会释放这个 UITouch 对象。

变更响应链

你通过改写响应者对象的 next 成员去改变响应链。这么做时,下一个响应者就是你返回的对象。

许多 UIKit 类已经改写了这个 property。

  • UIView:当 view 是一个 view controller 的根视图时,next responder 就是这个 view controller,否则,next responder 则是它的父元素
  • UIViewController:如果这个 view controller 的 view 是 window 的根视图时,next responder 就是 window 对象;如果该 view controller 是通过另一个 view controller 呈现的话,next responder 将是呈现它的 view controller
  • UIWindow:next responder 是 UIApplication 对象
  • UIApplication:next responder 是继承于 UIResponder 的 app delegate