iOS 事件传递和事件响应

2,205 阅读3分钟

事件的产生

iOS中事件分为:触摸事件(Touch Events)、运动事件(Motion Events)、远程事件(Remote Events),其中最常用的事件应该就是Touch Events了。

  • 触摸事件:点击、滑动等;
  • 运动事件:重力感应、摇一摇等;
  • 远程事件:耳机上的按键控制手机等;

当我们点击屏幕就会产生触摸事件,而系统会将该事件加入到有Application管理的事件队列中,使用队列的形式是因为队列的特点是FIFO,即先进先出,先产生的事件先处理才符合规则。Application会取出队列中最前面的事件分发到程序的主窗口。

事件传递

在Application将事件分发到主窗口后就会向下查找最适合处理该事件的视图。 Application事件队列->程序主窗口->向下查找最适合处理事件的View 在查找最合适处理事件的视图过程中有两个非常重要的函数:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

事件是如何找到最适合处理该事件的方法了?

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;函数是返回可以响应该事件的视图,如果返回不为空则该视图可以处理该事件,相反返回空则不能处理该事件

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;函数返回产生事件的坐标是否在该视图坐标系中。返回YES,代表产生事件的点在该视图坐标系上,说明该视图可以处理该事件;返回NO,代表产生事件的点不在该视图坐标系中,说明该视图不能处理该事件。

理解了以上两个函数的作用,我们就能更好的理解事件传递的过程。 事件被分发到程序主窗口也就是UIWindow上后,会调用- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event两个函数已确定当前视图能不能处理该事件。当两个函数都满足条件(即hitTest函数返回不为空,pointInside返回YES)时,事件会向子视图传递,直到子视图没有符合条件的视图,就认为自己是最适合处理该事件的视图。 视图不能接受触摸事件有一下几种情况:

  1. 不允许交互,userInteractionEnabled = NO
  2. 设置隐藏,父视图设置了隐藏,或者当前视图设置了隐藏 hidden = YES
  3. 透明度为00 ~ 0.01,当视图的透明度在00 ~ 0.01之间的时候是不能接收事件。

事件响应

理解事件的响应之前,我们先要了解一个比较重要的概念——响应者对象(UIResponse)。 在iOS中只有继承自UIResponse的对象才能接收并处理事件,我们称其为响应者对象。 在找到事件的最佳响应者之后会调用touchesBegantouchesMovedtouchedEnded等方法。touch方法默认会将事件沿着响应链向下传递(touch方法默认不处理事件,只传递事件),事件传递给下一个响应者处理。也就是事件会沿着响应者链传递知道被处理或者传递到UIApplication被抛弃掉。

我们知道iOS程序中视图是前后放置的,即视图之间是有前后关系的。事件的响应则是沿着视图前后放置的链条关系传递。响应者链的关系图可以参考下图:

从上图我们可以知道当前view是控制器的view时,那么控制器就是下一个响应者,如果当前view不是控制器的view,那么它的父视图就是下一个响应者,事件就会向父视图传递。事件在传递过程中如果都没有处理,则会传递给根视图UIWindow。如果UIWindow不能处理事件则会传递个UIApplication对象。如果UIApplication不能处理,事件则会被抛弃。

以上是我对事件传递和事件响应的简单理解,如有不对的地方请各位指正。