使用响应者和响应者链来处理事件

Posted by CoderLeonidas on July 19, 2019

使用响应者和响应者链来处理事件

学习如何处理在你的app内传递的事件

概述

app使用响应者对象来接受和处理事件。一个响应和对象可以是UIResponder类实例,或者UIView, UIViewController, UIApplication类的子类的实例。响应者接受原始的事件数据并且必须处理事件或者转发到其他响应者上。当你的app接受到一个事件,UIKit会自动将事件定向到最合适的响应者对象上去,也就是众所周知的第一响应者。

没有被处理的事件,会在活跃的响应者链中的响应者之间传递,而这是你app中响应者对象动态配置的。图1 显示了一个界面中包含一个label、一个textfield、一个button和2个background view的app中的响应者。这个图也揭示了事件是如何遵循响应者链,从一个响应者传转移到下一个响应者的。

图1

如果textfield不处理这个事件,UIKit会将这个事件发送到textfield的父视图对象,然后是窗口的根视图。从根视图开始,响应者链会在将事件定向到窗口之前将其转移到拥有视图的控制器。如果窗口也不处理事件,UIKit会转发事件到UIApplication对象。如果这个对象是UIResponder的实例并且也不是响应者链的一部分,则事件会被传递到App delegate。

决定一个事件的第一响应者

UIKit根据事件的类型将某一对象指定为此事件的第一响应者。 事件类型包括:

事件类型 第一响应者
触摸(Touch)事件 发生触摸的视图
按压(Press)事件 拥有焦点的的对象
摇动(Shake-motion)事件 你或者UIKit指定的对象
远程控制(Remote-control )事件 你或者UIKit指定的对象
编辑菜单的消息(Editing menu messages) 你或者UIKit指定的对象

注意

与加速度计、陀螺仪和磁力计有关的运动事件不遵循响应程序链。 相反,CoreMotion会将这些事件直接传递到指定的对象。 有关更多信息,请参见CoreMotion框架。

控件通过Action消息来与他们所关联的target对象进行通信。当用于与一个控件进行交互时,控件会发送action消息给这个控件的target对象。action消息并不是事件,但他们或许依然会利用响应者链。当控件的target对象是nil时,UIKit从target对象开始并遍历响应者链直到它找到一个实现了合适的action方法的对象。比如说,UIKit的编辑菜单就使用此行为来搜索响应者对象,而这些对象实现了诸如cut:, copy:,或者 paste:等等这样的方法。

手势的识别器(Gesture recognizers)在他们所在的视图之前接受到按压或者触摸的事件。如果视图手势识别器识别一连串的触摸手势失败,UIKit会将触摸手势发送到视图。如果视图无法处理触摸,则UIKit会将其向上传递到响应者链。有关使用手势识别器处理事件的更多信息,请参见Handling UIKit Gestures.

确定哪个响应者包含触摸事件

UIKit使用基于视图的hit-testing来确定触摸事件发生在哪里。具体来说,UIKit将触摸的位置与视图层次结构中视图对象的边界进行比较。UIView的hitTest:withEvent:方法遍历视图层次结构,查找包含指定触摸的最深子视图,而该子视图会成为触摸事件的第一响应者。

注意

如果一个触摸的位置在视图的边界之外,则hitTest:withEvent:方法会忽略这个视图及其所有子视图。因此,如果视图的clipsToBounds属性为NO,则即使该视图恰好包含触摸,也不会返回该视图范围之外的子视图。 有关hit-testing的更多信息,请参见UIView中关于hitTest:withEvent:方法的讨论。

发生触摸时,UIKit会创建一个UITouch对象并将其与视图关联。 随着触摸位置或其他参数的更改,UIKit会使用新信息更新这个UITouch对象。 唯一不变的属性是视图。(即使触摸位置移到原始视图之外,触摸的view属性中的值也不会更改。)触摸结束时,UIKit会释放UITouch对象

改变响应者链

您可以通过覆盖响应者对象的nextResponder属性来更改响应者链。 执行此操作时,下一个响应者是您返回的对象。

许多UIKit类已经重写此属性并返回特定的对象,包括:

  • UIView 对象。如果这个视图是某个viewcontroller的根视图,则nextResponder就是这个viewcontroller;否则就是这个view的父视图

  • UIViewController 对象。
    • 如果viewcontroller的视图是窗口的根视图,则nextResponder是窗口对象
    • 如果viewcontrollerA是由另一个viewcontrollerB呈现的,则nextResponder是呈现视图控制器viewcontrollerB。
  • UIWindow 对象。窗口对象的nextResponder 是UIApplication对象

  • UIApplication 对象。nextResponder是App 代理(app delegate),但仅当App 代理是 UIResponder的实例并且不是一个view、viewcontroller、或者UIApplication对象自身。

原文链接

Using Responders and the Responder Chain to Handle Events