美文网首页
iOS 响应者链

iOS 响应者链

作者: reboot_q | 来源:发表于2020-04-27 14:38 被阅读0次

    概述


    iOS 响应者链(Responder Chain) 是支持App界面交互的重要基础, 点击, 滑动, 旋转, 摇晃等都离不开其背后的响应者链.

    响应者 (Responder)


    当我们触控手机屏幕时, 系统便会将这一操作封装成一个UIEvent 放到事件队列中, 然后Application 从事件队列中取出这个事件, 按着需要找到去响应这个事件的最佳视图就是 Responder.

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

    首先从UIWindow开始调用hitTest, 然后来到RootView的hitTest & pointInside. 继续遍地rootView子视图(从后添加的view开始), 目标视图的superView hitTest 返回 superView, pointInside 返回 YES; 同级非目标视图hitTest 返回 nil, pointInside 返回 NO; 目标视图hitTest 返回 目标视图, pointInside 返回 YES, 最终找到响应视图!

    • 结论
    1. 寻找事件的最佳响应者是通过 hitTest & pointInside 完成的;
    2. hitTest 调用顺序是从UIWindow开始, 对视图每个子视图依次调用, 子视图的调用顺序是从后往前, 即后添加的先调用
      3.遍历子视图知道找到响应视图, 然后逐级返回最终到UIWindow返回此视图.

    处理者


    在上面我们找到了点击事件的响应者, 但是我们未添加响应的点击处理逻辑(tapGestureRecognizer), 所以目标视图不会处理.
    能够处理UI事件都是继承UIResponder的子类对象, UIResponder 主要有以下四个方法来处理事件:

    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
    - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
    

    如果某个Responder 没有处理事件, 事件会被传递. UIResponder 对象都有一个nextResponder 属性, 用于返回响应者链(Responder chain)中的下一个事件处理者, 如果所有Responder都不处理事件, 那么事件将会被丢弃.

    // 打印响应者链
     UIResponder *nextResponder = self.view3.nextResponder;
        NSMutableString *pre = [NSMutableString stringWithString:@"--"];
        NSLog(@"View3");
        while (nextResponder) {
            NSLog(@"%@%@", pre, NSStringFromClass([nextResponder class]));
            [pre appendString:@"--"];
            nextResponder = nextResponder.nextResponder;
        }
    
    image.png

    下一个响应者规则:
    如果有父视图则nextResponder为父视图, 如果是控制器根视图则指向控制器, 如果控制器在导航控制器中则指向导航控制器, 如果是根控制器则指向UIWindow, UIWindow的nextResponder指向UIApplication, 最后指向APPdelegate, 它们实现这一套是通过重写nextResponder实现的.

    • 结论
      1.找到最合适的响应视图后, 事件会从此视图开始沿着响应者链nextResponder传递, 直到处理事件的视图, 如果没有响应者处理事件则丢弃.
      2.如果视图有父视图则nextResponder指向父视图, 如果是根视图则指向控制器, 最终指向APPdelegate, 他们都是通过重写nextResponder 来实现的.

    无法响应的情况


    不会调用hitTest的情况

    • alpha == 0,
    • 子视图超出父视图,
    • userInteractionEnabled == NO
    • hidden == YES

    Runloop 处理点击事件

    Runloop 是app 运行的基础机制, 它一直处于接收消息-> 等待-> 处理的循环中, 当没有事件处理时会处于休眠状态, 等待下一个事件到来的唤醒去处理事件.
    当一个触摸事件发生后, 首先是由IOKit.framwork 生成一个IOHIDEvent事件并由Springboard接收, 然后springboard会通过mach port 将事件转发给 app 进程, 然后触发app注册在runloop中的source1来处理事件, source1 会触发 __IOHIDEventSystemClientQueueCallBack回调, 回调后又触发 source0, 在后面就是 UIApplication从事件队列中取出事件分发.

    相关文章

      网友评论

          本文标题:iOS 响应者链

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