美文网首页iOS程序犭袁
事件处理以及响应者链条(三)

事件处理以及响应者链条(三)

作者: RealSlimAlan | 来源:发表于2015-10-14 08:53 被阅读162次

    事件的产生和传递

    先调用hittest找到最适合响应的控件,然后再用touchbegan把事件往下传

    完整过程

    1> 先将事件对象由上往下传递(由父控件传递给子控件),找到最合适的控件来处理这个事件。

    2> 调用最合适控件的touches….方法

    3> 如果调用了[super touches….];就会将事件顺着响应者链条往上传递,传递给上一个响应者

    4> 接着就会调用上一个响应者的touches….方法

    事件的发生

    苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()。

    当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考这里。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。

    _UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。

    包装成UIEvent后,系统会将该事件加入到一个由UIApplication管理的事件队列中

    UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)

    主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步

    第一步的具体流程,控件不断调用以下代码:

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
    //判断当前状态是否不能响应事件
        if(self.hidden == YES || self.alpha <= 0.01 || self.userInteractionEnabled == NO){
            return nil;
        }else if ([self pointInside:point withEvent:event] == NO){//判断是否包含这个点,没有就返回
            return nil;
        }else{//继续遍历
            return [super hitTest:point withEvent:event];
        }
    }
    

    hitest方法

    
    4> 03-hitText方法和pointInside方法(02-事件传递代码)
        * (了解hitText)学习一个方法必须了解:什么时候调用和这个方法有什么用
        1. hitText什么时候调用:当一个事件传递给一个控件的时候,控件就会调用这个方法
        2. hitText作用: 寻找到最合适的view。
        * (回顾下事件传递),UIApplication -> UIWindow
        *  UIWindow去寻找最合适的view? [UIWindow hitTest:withEvent:]里面做了什么事情?
        1> 判断窗口能不能处理事件? 如果不能,意味着窗口不是最合适的view,而且也不会去寻找比自己更合适的view,直接返回nil,通知UIApplication,没有最合适的view。
        2> 判断点在不在窗口
        3> 遍历自己的子控件,寻找有没有比自己更合适的view
        4> 如果子控件不接收事件,意味着子控件没有找到最合适的view,然后返回nil,告诉窗口没有找到更合适的view,窗口就知道没有比自己更合适的view,就自己处理事件。
        * 验证下hitTest方法返回nil,里面的子控件能处理事件吗? 重写根控制器view的hitTest:withEvent:方法,
        * 验证这个方法是否真能找到最合适的view?
        * 如果点击屏幕任何一个地方,都是白色的view,怎么做。直接返回白色的view,就不会继续去找白色view的子控件了。
        * pointInside作用:判断一个点在不在一个控件上
        * point参数:方法调用者坐标系上的点,PPT画图分析原理。
    

    示例

    Snip20150711_1.png

    触摸事件的传递是从父控件传递到子控件

    • 点击了绿色的view:
      UIApplication -> UIWindow -> 白色 -> 绿色
    • 点击了蓝色的view:
      UIApplication -> UIWindow -> 白色 -> 橙色 -> 蓝色
    • 点击了黄色的view:
      UIApplication -> UIWindow -> 白色 -> 橙色 -> 蓝色 -> 黄色

    如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件(掌握)

    具体流程如下:

    • 如何找到最合适的控件来处理事件?
    • 自己是否能接收触摸事件?
    • 触摸点是否在自己身上?
    • 从后往前遍历子控件,重复前面的两个步骤
    • 如果没有符合条件的子控件,那么就自己最适合处理

    三种情况下,控件不接收触摸事件

    1. 控件的userinterfaceEnable = NO;
    2. 透明度低于0.01
    3. 控件被隐藏了

    而且,某个控件隐藏,或者透明度低于0.01,它的子控件都会看不见,同样无法接受触摸事件, 同时,UIImageView的userinterfaceEnable默认为NO。

    触摸事件处理的详细过程,响应者链条

    用户点击屏幕后产生的一个触摸事件,经过一系列的传递过程后,会找到最合适的视图控件来处理这个事件

    找到最合适的视图控件后,就会调用控件的touches方法来作具体的事件处理
    touchesBegan…
    touchesMoved…
    touchedEnded…

    这些touches方法的默认做法是将事件顺着响应者链条向上传递,将事件交给上一个响应者进行处理

    响应者链条示意图

    响应者链条:是由多个响应者对象连接起来的链条
    作用:能很清楚的看见每个响应者之间的联系,并且可以让一个事件多个对象处理。
    响应者对象:能处理事件的对象


    Snip20150711_3.png

    在顶级视图(key window的视图)上调用pointInside:withEvent:方法判断触摸点是否在当前视图内;
    如果返回NO,那么A返回nil;
    如果返回YES,那么它会向当前视图的所有子视图(key window的子视图)发送hitTest:withEvent:消息,遍历所有子视图的顺序是从subviews数组的末尾向前遍历(从界面最上方开始向下遍历)。
    如果有subview的hitTest:withEvent:返回非空对象则A返回此对象,处理结束(注意这个过程,子视图也是根据pointInside:withEvent:的返回值来确定是返回空还是当前子视图对象的。并且这个过程中如果子视图的hidden=YES、userInteractionEnabled=NO或者alpha小于0.1都会并忽略);
    如果所有subview遍历结束仍然没有返回非空对象,则A返回顶级视图;

    相关文章

      网友评论

        本文标题:事件处理以及响应者链条(三)

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