美文网首页
iOS事件的响应机制和传递

iOS事件的响应机制和传递

作者: 牛奶红茶 | 来源:发表于2020-12-02 11:30 被阅读0次

    主要讲解用户触摸或点击手机屏幕后产生的事件是如何派发传递的,如何查找到事件的第一响应者控件,以及找到响应者后事件是如何通过响应者链向下传递的,直到事件被接收并作出具体处理或者被废弃

    一,相关概念

     >.第一响应者:

            第一响应者一般指的是用户当前触摸的响应者对象,表示当前对象正在与用户交互,第一响应者是响应者链的开端

        响应者链和事件分发传递的使命是找出第一响应者

    >响应者对象:

      具有相应和处理iOS事件能力的对象,也就是继承UIResponder的类的对象,我们常用的UIApplication,UIWindow,UIViewController,UIView都是继承或者间接继承UIResponder类,所以他们的实例对象都可以成为响应者对象

    类的继承关系

     
    >响应者链:

      由多个不同响应者对象链接起来构成的一个链条:

           响应者链可以看作是链表,整体是一个树,由于每个节点都是一个响应者对象,每个响应者对象都存有指向下一个响应者的指针nextResponder,可以通过nextResponder找到下一个responder,直到找到第一响应者响应了事件就会停止传递,如果最终没有响应者响应事件,那么该事件就会被废弃

    二,iOS中的时间类型

     >iOS事件主要分为三大类:

    1.Touch Event(触摸事件)->用户触摸屏幕产生的交互事件

    2.Motion Event(运动事件)  -> 运动事件也叫加速计事件,这类事件事依赖手机里的加速计,陀螺仪等硬件传感器实现的.用户在摇晃手机,倾斜手机后就会产生这类事件.可用于屏幕旋转控制

    3.Remote-ControlEvent(远程控制事件) ->远程控制事件指的是用户在操作多媒体的时候产生的事件,例如播放音乐时后台播放控制

    三.如何控制控件不能响应事件:

     >1.设置不允许交互:设置控件的userInteractionEnnabled = NO;

    >2.设置控件隐藏:将控件的hidden设置为YES,隐藏控件

    >3.设置透明度:设置控件的透明度alpha<0.01,放alpha的值在0.0~0.01之间控件为透明

    >.4超出父控件相应区域

      !!如果view被设置为透明,那么会直接影响其子view的透明度;如果view无法响应事件,那么这个view上的所有subview都不可响应事件,也就是说如果父控件不能接受触摸事件,那么子控件就不可能接受到触摸事件

    四.事件的产生和分发传递

    view层级图

    >事件是如何产生 ?

     当用户触摸屏幕时,系统会检测到屏幕上的压力感知到触摸事件,iOS系统检测到触摸操作后会将这个事件打包成一个UIEvent对象,并将该事件加到一个由UIApplication管理的时间队列中,然后UIApplication会从时间队列中取出触摸事件并传递给UIWindow处理,keyWindow会使用hitTest:withEvent:方法寻找一个最合适的响应者来处理事件,一般寻找到的合适处理事件的控件是touch操作初始点的视图,找到合适第一响应者的视图之后,救会调用该视图控件的touches方法来处理具体的事件,这个过程称为hit-test

    >处理事件的方法

    事件处理的几个方法

    >事件是如何传递的?

    事件的传递是由父控件向子控件传递的,例如上面的view层级图,viewA,viewB,viewE被添加到rootView中,viewC,viewD是viewB的子view,用户点击viewC的事件传递链是

    事件传递链

    传递方向:由底层系统向可以响应事件的控件传递

    UIKit -> UIApplication的事件队列 ->keyWindow ->rootView -> 一些subView->事件影响者view

    >如何查找第一响应者?

    主要方法

    命中测试

    只要事件传递给一个控件,那么这个控件就会调用自己的hitTest:withEvent:方法,他的作用是寻找并返回适合响应处理事件的第一响应者

    判断点是否在控件上

    作用是判断点在不在当前view上(方法调用者的坐标系上),如果返回YES,代表在方法调用者的坐标系上,返回NO代表点不在该方法调用者的坐标系上,那么方法调用者也就不能处理事件


     流程图

    >1.主窗口收到应用程序传递过来的事件后,首先判断自己能否就收触摸事件,如果能,那么再判断触摸点在不在窗口的范围内

    >2.如果触摸点在窗口上,那么窗口会从后往前遍历自己的子控件,遍历自己的子控件只是为了找出最合适的view(事件响应者)

    >3.遍历到每一个子控件后,又会重复上面的两个步骤,将事件传递给子控件,点判断子控件是否能接受事件,再判断触摸点是否在子控件的范围内

    >4.如此循环遍历子控件,直到找出合适响应事件的第一响应者,如果没有更合适的子控件,那么自己就成了最合适的view

    >>hitTest:withEvent方法中如何处理的?

    1.首先判断当前视图是否可响应事件,也就是判断当前视图的是否可交互状态,隐藏状态,透明度

    2.如果当前视图允许响应触摸事件,则调用当前视图的pointInside:withEvent:方法判断触摸点是否在当前视图内

    3.若pointInside:withEvent:返回NO,则hitTest:withEvent:返回nil

    4.若pointInside:withEvent:返回YES,则向当前视图的所有子视图发送hitTest:withEvent:消息,所有子视图   的遍历顺序是从最顶层的视图一直到最底层视图,即从subviews数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕

    5.若第一次有子视图返回非空对象,则hitTest:withEvent:方法返回此对象,处理结束,如果所有的子视图都返回nil,则hitTest:withEvent:方法返回该视图本身

    6.找到合适的第一响应者,就会调用该控件的touches系列方法处理具体的事件,如果找不到第一响应者就不会调用touches系列方法

    >>查找响应者实例

    假设用户点击viewC后的处理流程

    1.rootView为window的跟视图,窗口会首先对rootView进行hit-Test,判断结果为用户点击位置在rootView的范围内

    2.继续检测rootView的子控件(viewA,viewB,viewE),相应的调用自己的hit-Test方法,检测到viewA,viewE的pointInside:withEvent:返回NO,则点击范围不在viewE,viewA内,对应的hitTest:withEvent:返回nil,这时rootView继续检测viewB的hit-Test方法,viewB的pointInside:withEvent:返回yes,确定点击范围在viewB内

    3.这时viewB内存在viewC和viewD两个子控件,viewD在viewC之后添加到viewB的subViews中,因此优先检测viewD的hit-Test方法,viewD的pointInside:withEvent:返回NO,对应的hitTest:withEvent:返回nil,说明点击不在viewD内,viewD及其子控件都不可响应事件。因此需要回溯检测viewC的hit-Test方法;

    4.viewC的pointInside:withEvent:返回YES,说明点击范围在viewC范围内,由于viewC没有子控件,也可以理解为viewC的子控件hit-Test返回了nil

    5.因此viewC的hitTest:withEvent:将会返回viewC,viewB的hitTest:withEvent:返回viewC,rootViewhitTest:withEvent:将会返回viewC

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

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

    >>hitTest:withEvent:方法底层实现

    底层实现

    五.事件响应

    >响应链的传递方向

    由是第一响应者的控件向系统传递,

    事件响应view→superView→rootVIew→viewController→window→Application→AppDelegate

    >响应者链的关系图:

    响应者链关系图

    解释说明:

    1、响应者链是由多个响应者对象构成的链条,每个响应者对象必须是继承UIResponder类的子类;

    2、如果View是控制器VC的View,那么VC就是view的nextUIResponder;

    3、如果View不是控制器VC的View,那么此View的superView为当前view的nextUIResponder;

    4、视图控制器VC的nextUIResponder是控制器view(VC.View)的superView,即VC.nextUIResponder = VC.View.superView,如下图;

    5、如果在视图层都不能处理事件,则将事件传递个UIWindow进行处理;

    6、Window的nextResponder是UIApplication,如果window也不处理事件,则将事件传递给UIApplication;

    7、UIApplication的nextResponder是AppDelegate,如果UIApplication也不能处理该事件,则将此事件丢弃

    输出的log 显示VC.nextUIResponder = VC.View.superView

    六,end

    1.当用户点击页面上一个view的时候,系统只是检测到用户点击触摸了屏幕,而此时无法确认用户触摸的view控件,因此需要根据事件分发传递的逻辑寻找到可以响应事件的第一响应者控件;

    2.如果需要处理特殊的需求,例如单击不规则按钮事件、点击事件穿透等问题时可以重写主要方法hitTest:withEvent:和pointInside:withEvent:来处理;

    3.发生了触摸或其他事件后,系统将事件打包成UiEvent发送到UIApplication管理的事件队列中,UIApplication从队列中取出最前面的事件分发下去;

    4.如果找到了合适处理事件的控件,会调用此控件的touchs系列方法,如果响应事件的控件调用了 super touchs等方法,那么事件会沿着响应链向下传递,传递给下一个响应者,这个响应者来调用touchs系列方法

    5.如果父视图不接收处理事件,那么他的子视图也不能接收到;

    6.事件传递是由父控件向子控件传递的,事件响应是由子控件向父控件出啊低的;

    相关文章

      网友评论

          本文标题:iOS事件的响应机制和传递

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