网上很多关于这方面的文章,但是决定还是如何使用响应者和响应者链出发,然后来解释一波儿,首先推荐去看官方文章Using Responders and the Responder Chain to Handle Events
本篇是对文章的翻译
概述
应用程序使用响应对象接收和处理事件。UIResponder
。类的任何实例对象都是响应者,公共子类包括UIView
,UIViewController
,UIApplication
,响应者接收原始事件数据,并且必须处理事件或将其转发给另一个响应者对象。 当您的应用收到事件时,UIKit会自动将该事件定向到最合适的响应者对象,称为第一响应者。未处理的事件从响应者传递到活动响应者链中的响应者,这是应用程序的响应者对象的动态配置。 图1显示了应用程序中的响应者,其界面包含label,文本 text field,button两个background views。 该图还显示了事件如何在响应者链之后从一个响应者移动到下一个响应者。
如果text field不处理事件,UIKit
会将事件发送到文本字段的父UIView对象,然后是window
的根视图。 从根视图开始,响应器链在将事件定向到窗口之前转移到拥有的视图控制器。 如果window
无法处理事件,UIKit会将事件传递给UIApplication
对象,如果该对象是UIResponder
的实例而不是响应者链的一部分,则可能传递给app delegate。
确定事件的第一响应者
UIKit
根据事件的类型将对象指定为事件的第一响应者。 事件类型包括:
- 触摸事件(Touch events):第一响应者是触摸点所在的视图。
- 按压事件(Press events):第一响应者是有焦点的响应者。
- 摇晃运动事件(Shake-motion events):第一响应者是由我们自己(或者UIKit)指定为第一响应者的对象。
- 远程控制事件(Remote-control events):第一响应者是由我们自己(或者UIKit)指定为第一响应者的对象。
- 编辑菜单消息(Editing menu messages):第一响应者是由我们自己(或者UIKit)指定为第一响应者的对象。
注意:与加速计、陀螺仪和磁力计相关的运动事件不遵循响应者链,
Core Motion
会将这些事件直接传递给我们指定的对象。有关更多信息,可以参看Core Motion Framework。
控件使用动作消息直接与其关联的目标对象进行通信。 当用户与控件交互时,控件会向其target
对象发送操作消息。 动作消息不是事件,但它们仍然可以利用响应者链。 当控件的target
对象为nil时,UIKit
从target
对象开始并遍历响应者链,直到找到实现相应操作方法的对象。 例如,UIKit
编辑菜单使用此行为来搜索响应器对象,这些对象实现具有cut:,copy:
或paste:
等名称的方法。
手势识别器在其视图之前接收touch
和press
事件。 如果视图的手势识别器无法识别触摸序列,则UIKit
会将touch
发送到视图。 如果视图没有处理touch
,UIKit
会将它们传递给响应者链。 有关使用手势识别器处理事件的更多信息,请参阅处理UIKit手势。
确定哪个响应者包含touch
事件
UIKit
使用基于视图的hit-testing
来确定$touch
事件发生的位置。 具体来说,UIKit
将touch
位置与视图层次结构中视图对象的边界进行比较。
UIView
的 hitTest:withEvent:
方法遍历视图层次结构,查找包含指定触摸(touch)的最深子视图,该子视图成为触摸事件的第一个响应者。
触摸发生时,UIKit
会创建一个UITouch
对象并将其与视图关联。 当触摸位置或其他参数发生变化时,UIKit
会使用新信息更新相同的UITouch
对象。 唯一不改变的属性是视图。 (即使触摸位置移动到原始视图之外,触摸视图属性中的值也不会改变。)触摸结束时,UIKi
t会释放UITouch
对象。
hitTest:withEvent: 方法介绍
- 如果触摸位置位于视图边界之外,则
hitTest:withEvent:
方法将忽略该视图及其所有子视图。 因此,当视图的clipsToBounds
属性为NO时,即使它们恰好包含触摸,也不会返回该视图边界之外的子视图。
2.hitTest:withEvent:
方法会遍历当前视图层,并调用每个子视图的pointInside:withEvent:
方法来判断子视图的边界是否包含触摸点。如果pointInside:withEvent:
返回YES,则会同样遍历子视图的视图层,直到找到包含指定点的最上层的视图。如果视图不包含该触摸点,就忽略此视图层次结构的分支。因此我们可以覆override
hitTest:withEvent
以隐藏子视图中的触摸事件。 - 此方法忽略掉被隐藏,禁用用户交互或alpha级别小于0.01的视图对象。在确定命中时,此方法不会考虑视图的内容。因此,即使指定的点位于该视图内容的透明部分,仍然可以返回视图。
位于接收者界限之外的点永远不会被报告为命中,即使它们实际上位于接收者的子视图中。如果当前视图的clipsToBounds
属性设置为NO并且受影响的子视图超出视图的边界,hitTest:withEvent:
方法也不会返回命中了此视图。
改变响应者链
您可以通过覆盖响应程序对象的nextResponder
属性来更改响应程序链。 执行此操作时,下一个响应者是您返回的对象。
许多UIKit类已经覆盖此属性并返回特定对象,包括:
- 对象。 如果视图是视图控制器的根视图,则下一个响应者是视图控制器; 否则,下一个响应者是视图的父视图。
-
UIViewController
对象。
如果视图控制器的视图是window的根视图,则下一个响应者是window object
。
如果视图控制器由另一个视图控制器呈现,则下一个响应者是呈现视图控制器。 -
UIWindow
对象。 窗口的下一个响应者是UIApplication
对象。 -
UIApplication
对象。 下一个响应者是UIApplication
,但仅当应用程序委托是UIResponder的实例且不是视图,视图控制器或应用程序对象本身时。