事件机制理解 一

目录:

1.事件分类
2.响应者链(Responder Chain)
3.事件分发(Event Delivery)


内容:

1.事件分类:

  • 触控事件(Touch Event)(单点、多点触控以及各种手势操作)
  • 传感器事件(Motion Event)(重力、加速度传感器等)
  • 远程控制事件(Remote-Control Event)(远程遥控iOS设备多媒体播放等)

2.响应者链(Responder Chain)

UIResponder是所有响应对象的基类,在UIResponder类中定义了处理上述各种事件的接口。我们熟悉的UIApplication、 UIViewController、UIWindow和所有继承自UIView的UIKit类都直接或间接的继承自UIResponder,所以它们的实例都是可以构成响应者链的响应者对象。

响应者链就是由一系列的响应者对象构成的一个层次结构。

  1. 响应者链通常是由视图(UIView)构成的;
  2. 一个视图的下一个响应者是它视图控制器(UIViewController)(如果有的话),然后再转给它的父视图(Super View);
  3. 视图控制器(如果有的话)的下一个响应者为其管理的视图的父视图;
  4. 单例的窗口(UIWindow)的内容视图将指向窗口本身作为它的下一个响应者
  5. 单例的应用(UIApplication)是一个响应者链的终点,它的下一个响应者指向nil,以结束整个循环。

3.事件分发(Event Delivery)

3.1 第一响应者(First responder)

指的是当前接受触摸的响应者对象(通常是一个UIView对象),即表示当前该对象正在与用户交互,它是响应者链的开端。

整个响应者链和事件分发的使命都是找出第一响应者。

我们可以控制一个View让其成为First Responder,通过实现 canBecomeFirstResponder方法并返回YES可以使当前View成为第一响应者,或者调用View的becomeFirstResponder方法也可以,例如当UITextField调用该方法时会弹出键盘进行输入,此时输入框控件就是第一响应者。

3.2 hit-test view

hit-test view(将触摸事件传递给其处理的视图)过程:

  1. iOS系统检测到手指触摸(Touch)操作时会将其打包成一个UIEvent对象,并放入当前活动Application的事件队列,
  2. 单例的UIApplication会从事件队列中取出触摸事件并传递给单例的UIWindow来处理,
  3. UIWindow对象首先会使用hitTest:withEvent:方法寻找此次Touch操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图
  4. hitTest:withEvent:,方法会在其视图层级结构中的每个视图上调用pointInside:withEvent
  5. pointInside:withEvent:方法判断触摸点是否在当前视图内
    • 若返回NO,则hitTest:withEvent:返回nil;
  6. 若返回YES,则向当前视图的所有子视图(subviews)发送hitTest:withEvent:消息
  7. 所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕;
    • 如所有子视图都返回非,则hitTest:withEvent:方法返回自身(self)。
  8. 若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束;

至此,本次点击事件的第一响应者就通过响应者链的事件分发逻辑成功的找到了。

NOTE:

  1. 如果最终hit-test没有找到第一响应者,或者第一响应者没有处理该事件,则该事件会沿着响应者链向上回溯,如果UIWindow实例和UIApplication实例都不能处理该事件,则该事件会被丢弃;

  2. hitTest:withEvent:方法将会忽略一些视图:

    • 隐藏(hidden=YES)的视图,
    • 禁止用户操作(userInteractionEnabled=YES)的视图,
    • 以及alpha级别小于0.01(alpha<0.01)的视图。
    • 如果一个子视图的区域超过父视图的bound区域(父视图的clipsToBounds 属性为NO,这样超过父视图bound区域的子视图内容也会显示),那么正常情况下对子视图在父视图之外区域的触摸操作不会被识别,因为父视图的pointInside:withEvent:方法会返回NO,这样就不会继续向下遍历子视图了。
posted @ 2016-04-28 14:07  孙焱焱  阅读(256)  评论(0编辑  收藏  举报