iOS触摸事件响应链剖析

作者: 墨痕未干 | 来源:发表于2017-02-23 15:10 被阅读1056次

    最近优化项目,遇到一个需求。类似就是:两个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

    相关文章

      网友评论

        本文标题:iOS触摸事件响应链剖析

        本文链接:https://www.haomeiwen.com/subject/kvhbwttx.html