iOS-Events[2]-The Responder Chain

本文对Responder Chain的机制进行讲解。

iOS系统的Events类型分为以下几种:Multitouch events, Accelerometer events, Remote control events。

Events

The Responder Chain

当iOS系统识别一个Event,它会首先将其传递给对可能相关的对象,例如触碰的View,如果该View无法处理,则往更外层传递,直到找到一个可以处理的相应对象,这一设计模式成为响应链(The Responder Chain)

当一个用户交互事件发生时,UIKit会创建一个Event对象,包含其信息来处理该事件,然后将其放置到App的Event队列中,UIApplication对象从队列中取出该事件,并对其进行分发。一般情况下,会发送给Key Window对象,Key Window对象再对其进行分发:

  • 如果是Touch Events,则会分发到触碰发生的View(Hit-Test View)上;
  • 如果是Motion and Remote Control Events,则会分发到第一响应者(First Responder)上。

Hit-Testing

Hit-Testing是iOS系统用于寻找到触碰View的方式。它通过递归subViews的方式,根据触碰的点是否在View的Bounds中,来判断最底层的触碰View,例如下图中:

Hit-Testing

触碰点在A中,遍历subViews,不在B中,在C中,则继续遍历subViews,不在D中,在E中,E没有subViews,确定Hit-Testing View就是E。

Hit-Testing的方法为:

1
2
hitTest:withEvent:
pointInside:withEvent:

如果Hit-Testing View无法处理该事件,则会通过Responder Chain来传递,寻找到一个可以处理该事件的对象。

Responder Chains

许多Events类型的分发,是基于Responder Chain来完成的。The Responder Chain是由一系列连接在一起的Responder Objects构成的。The Responder Chain中的第一个为The First Responder,最后一个为UIApplication

The Responder Chain的传递路径分为两种,分别如下,左图是ViewController间没有发生View嵌套的,右图是发生了嵌套的:

Events_Handling

The Responder Chain不仅仅处理Events,还包含其他用途:

  • Touch Events:如果Hit-Testing View无法处理,则会通过The Responder Chain传递该事件;
  • Motion Events:The First Responder必须实现motionBegan:withEvent:或者motionEnded:withEvent:方法;
  • Remote Control Events:The First Responder必须实现remoteControlReceivedWithEvent:方法;
  • Action Messages:用户点击UIButton或者UISwitch,而其Target没有设置时,该消息会通过The Responder Chain传递;
  • Editing-menu Messages:用户点击剪切、复制和粘贴面板时,该消息会通过The Responder Chain传递;
  • Text Editing:用户点击输入框时,该View会自动成为The First Responder,自此虚拟键盘会出现,但是也可以设置自定义的View来替代键盘出现,设置UITextField的InputView即可。

Responder Objects

Responder Objects是可以响应和处理事件的对象,基类为UIResponder,其包含了通用的相应行为,不单单事件处理。UIApplication, UIViewController和UIView都继承了该类。

注意,Core Animation layers不是Responders。

The First Responder是第一个处理事件的对象,一般情况下,是一个UIView。一个对象成为The First Responder可以通过以下两个方式:

  • 重写canBecomeFirstResponder方法,返回YES;
  • 接收becomeFirstResponder消息,在必要时,可以给自身发送这个消息。

注意:UIKit会在用户点击时,自动设置TextField和TextView为The First Responder,而其他的对象,则需要Apps去显式调用becomeFirstResponder来设置。另外,不要在没有绘画完成的时候,调用becomeFirstResponder,例如要在viewDidAppear中,而不要在viewWillAppear中发送,否则不会产生效果,会返回NO。