手记

iOS触摸事件响应链剖析

最近优化项目,遇到一个需求。类似就是:两个View,viewA在viewB上面,我要透过viewA能对viewB进行点击滑动操作。

简单粗暴的方法

在viewA这个类中,重写下面的方法。

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{  
}

这个方法的作用是用来判断,操作的触摸点是否在当前视图上。方法返回YES:触摸点在当前视图上,视图会响应事件。返回NO:触摸点不在当前视图上,视图不会响应事件。

还有,我们可以根据参数point来判断,进行点击区域的选择,可以实现,一个视图View,前半部分能响应点击,后半部分不响应点击。

所以viewA中重写这个方法让它返回NO,我们在点击到viewA上的时候,viewA不会响应,退而其次viewB会响应我们操作。这样就做到了穿透viewA对viewB进行操作。

为什么viewA不响应操作,viewB就行响应呢?这这涉及到响应链

响应者链剖析

首先说一下UIResponder,它响应用户的操作处理各种事件。UIView,UIViewController都是继承于它。而UIWindow,UILabel,UIImageView是继承于UIView。所以他们都能成为响应者,成为响应者链的一环。只有熟悉UIKit继承树,才能更好地理解响应者链。


UIKit继承树.jpg

当一个触摸事件产生后,系统会分为两步来处理:事件的传递+事件的响应

事件的传递

当触摸屏幕的时候,系统是这样传递这个触摸事件的:

  • 加入到一个由UIApplication管理的事件队列中(队列的特点是FIFO,即先进先出,先产生的事件先处理才符合常理,所以把事件添加到队列中)

  • UIApplication会发送事件给应用程序的主窗口UIWindow。

  • 主窗口UIWindow会调用hitTest:withEvent:方法在视图(UIView)层次结构中找到一个最合适的UIView来处理触摸事件(也就是把事件传递给那个最适合的UIView)

说一下这个方法:

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ 
}

它的工作流程是:

  • 首先调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内

  • 若pointInside:withEvent:方法返回NO,说明触摸点不在当前视图内,则当前视图的hitTest:withEvent:返回nil

  • 若pointInside:withEvent:方法返回YES,说明触摸点在当前视图内,则遍历当前视图的所有子视图(subviews),调用子视图的hitTest:withEvent:方法重复前面的步骤,子视图的遍历顺序是从top到bottom,即从subviews数组的末尾向前遍历,直到有子视图的hitTest:withEvent:方法返回非空对象或者全部子视图遍历完毕。

  • 若第一次有子视图的hitTest:withEvent:方法返回非空对象,则当前视图的hitTest:withEvent:方法就返回此对象,处理结束

  • 若所有子视图的hitTest:withEvent:方法都返回nil,则当前视图的hitTest:withEvent:方法返回当前视图自身(self)

总结;每个view都有这个方法,用来处理用户的操作事件。它返回:self,代表这个view会接受用户的操作事件,返回:nil,则代表这个view不会接受用户的操作事件。



从以上还可以看出,视图在项目中的结构是以树状形式存在的。

视图树状图.png

事件的响应

经过以上的事件的传递过程,事件已经传递给系统认为最适合的View了。接下来就是处理这个事件。
处理事件方法:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
}

但是最适合的,不一定就能一定能处理,如果,这个View不能处理这个事件则会将这个事件上抛,就是按照事件传递下来的路线上抛。

大致流程如下:

  • 不能处理将事件传递给其上级视图(View的superView);

  • 如果上级视图仍然无法处理则会继续往上传递;一直传递到视图控制器view controller;

  • 首先判断视图控制器的根视图view是否能处理此事件;如果不能则接着判断该视图控制器能否处理此事件,如果还是不能则继续向上传递;

  • 一直到 window,如果window还是不能处理此事件则继续交给application处理;

  • 如果最后application还是不能处理此事件则将其丢弃;

响应者链简单图解

响应者链图解.png



作者:墨痕未干
链接:https://www.jianshu.com/p/d8b8b3724440


0人推荐
随时随地看视频
慕课网APP