事件传递:响应者链条(译)

当你设计你的应用程序时,你可能需要动态的响应事件。例如,一个触摸事件可以由屏幕上的很多对象产生,你必须决定由哪个对象响应对应的事件和明白该对象如何接收到事件的。

当用户产生了一个事件,UIKit创建了一个包含处理事件所需信息的事件对象。然后把这个事件对象放置在全局事件队列。对于触摸事件,会被包装成一个UIEvent对象。对于动作事件,会根据你所使用的框架和你感兴趣的动作事件而有所不同。

一个事件沿着一个具体的路径向前传递,直到有一个对象可以对其进行处理。首先,

UIApplication单例对象会从事件队列里获取一个事件并把它分发,一贯地将事件发送到应用的key window对象,key window对象会把事件传递到原始对象进行处理,原始对象会根据事件的类型而有所不同。

· 触摸事件。对于触摸事件,window对象首先会尝试传递事件到产生触摸事件的视图。这个视图被称为hit-test视图,寻找hit-test视图的过程被称为hit-testing。

· 动作和远程控制事件。window对象会把摇一摇动作或者远程控制事件发送到第一响应者进行处理。

这些事件路径的最终目标是寻找一个对象可以处理和相应一个事件。因此,UIKit首先会事件发送到最适合处理该事件的对象。最适合处理触摸事件的对象就是hit-test视图,对于其他事件,对象就是第一响应者。接下来的部分会详细展开hit-test视图和第一响应者对象是如何被确认的。

Hit-Testing返回触摸事件发生的视图

iOS利用hit-tesing寻找被触摸的视图。hit-tesing需要检查一个触摸事件是否在相关的视图对象的范围内,如果是,它会递归地检查这个视图对象的所有子视图。处于视图层级中包含触摸点的最底层视图就会成为hit-test视图。当iOS确定hit-test视图后,它会把触摸事件抛给这个视图去处理。

例如,假如用户触摸视图E(图2-1)。iOS通过以下顺序从子视图中找出hit-test视图

1.触摸点在视图A里面,所以会检查子视图B和C。

2.触摸点不在视图B的范围内,但是在视图C的范围内,所以它会检查子视图D和E。

3.触摸点不在视图D,但在视图E的范围内。

视图E是在视图层级中包含触摸点的最底层视图,所以它就成为了hit-test视图

(图2-1)

(hitTest:withEvent:)方法返回一个给定CGPoint和UIEvent的hit-test视图。(hitTest:withEvent:)方法通过(pointInside:withEvent:)方法调用。如果一个点通过(hitTest:withEvent:)方法判断是在视图的范围内,(pointInside:withEvent:)方法就会返回YES。然后这个方法就会在返回YES的子视图上面递归地调用(hitTest:withEvent:)方法。

如果这个点通过(hitTest:withEvent:)方法判断不在视图的范围内,最先调用的(pointInside:withEvent:)方法就会返回NO。这个点被忽略了,(hitTest:withEvent:)方法就会返回nil。如果一个子视图返回NO,那么在视图层级中整个分支都会被忽略,因为如果这个触摸点不在这个视图中产生,那么也不会再这个视图的子视图中产生。这就意味着一些在父视图外的子视图的点不能接收到触摸事件,因为触摸点必须在父视图和子视图的范围内。子视图的范围超出父视图的情况会发生在子视图的clipsToBounds属性被设为NO。

注意:一个触摸对象的生命周期会跟它的hit-test密切关联,即使后来触摸移到view的外面。

hit-test视图有第一次机会去处理一个触摸事件。如果hit-test视图不能处理一个事件,这个事件就会在视图的响应者链条上传递,直到系统找到一个可以找到该事件的对象。

响应者链条由响应者对象组成

很多类型的事件依赖一个响应者链条进行传递。响应者链条是一系列的响应者对象。由第一个响应者开始,并由application对象结束。如果第一响应者不能处理一个事件,事件会向前传递给响应者链条的下一个响应者。

响应者对象是一个可以处理事件的对象。UIResponder类是所有响应者对象的基类,它定义的程序接口不只是为了事件的处理,而且为了普通的响应者行为。UIApplication,UIViewController和UIView的实例都可以成为响应者,意味着所有的视图和很多的关键控制器对象都可以是响应者。注意:核心动画图层都不可以成为响应者。

首先由第一响应者接收事件。一般的,第一项响应者是一个视图对象。一个对象通过以下两种方式成为第一响应者:

1、复写(canBecomeFirstResponder)方法并返回YES。

2、接收一个(becomFirstResponder)信息。如果有必要,一个对象可以发送这条信息给它自己。

注意:一个对象成为第一响应者前确保应用已经把这个对象的图层渲染出来。例如,我们一般在(viewDidAppear:)方法里面调用(becomeFirstResponder)方法,如果尝试在(viewWillAppear:)方法里面分配第一响应者,对象的图层还没有渲染出来,所以(becomeFirstResponder)方法就会返回NO。

事件不是唯一的依赖响应者链条的对象。响应者链条被用于以下的情况:

· 触摸事件。如果hit-test视图不能处理一个触摸事件,那么这个事件就会从hit-test视图开始沿着响应者链条传递。

· 动作事件。通过UIKit来处理摇一摇动作事件,第一响应者必须实现UIResponder类的(motionBegan:withEvent:)方法或者(motionEnded:withEvent:)方法之一。

· 远程控制事件。第一响应者处理远程控制事件必须实现UIResponder类的(remoteControlReceivedWithEvent:)方法。

· 动作信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 用户以多种方式操纵他们的iOS设备,例如触摸屏幕或摇动设备。 iOS会解释用户何时以及如何操作硬件并将此信息传递到...
    坤坤同学阅读 9,446评论 7 19
  • 好奇触摸事件是如何从屏幕转移到APP内的?困惑于Cell怎么突然不能点击了?纠结于如何实现这个奇葩响应需求?亦或是...
    Lotheve阅读 58,885评论 51 604
  • 在iOS开发中经常会涉及到触摸事件。本想自己总结一下,但是遇到了这篇文章,感觉总结的已经很到位,特此转载。作者:L...
    WQ_UESTC阅读 11,303评论 4 26
  • 本文来自:http://ios.jobbole.com/84081/ 前言: 按照时间顺序,事件的生命周期是这样的...
    HackerOnce阅读 7,831评论 1 10
  • 事件传递:响应者链 当你设计一个app的时候,你很可能需要你的app能够动态响应某些事件。比如,触摸可以发生在屏幕...
    hjfrun阅读 4,645评论 1 5