美文网首页
iOS事件传递+响应者链条(responseChain)模拟

iOS事件传递+响应者链条(responseChain)模拟

作者: mrChan1234 | 来源:发表于2018-08-16 11:40 被阅读0次

    在前面的文章中,我提到了事件传递和响应的U型传递,这里我们通过一个demo模拟来具体查看他的传递和响应顺序,例如点击一个按钮,iOS的后面具体做了些什么呢?

    首先我们看下图:


    repsonseChain.png

    我们点击蓝色的view,系统后面做了些什么操作呢?前面我们说了,我们用手指点击屏幕的时候,硬件接收到了这种touch操作,然后和iOS系统打交道,以source0的形式通知到UIApplication的currentRunLoop(其实也就是mainRunloop),runloop接收到source0事件,将该事件封装成一些对象(这些对象包含事件的一些信息,比如:UITouch、UIEvent)
    我们可以通过打断点看到这个调用过程:


    source0.png
    系统会把该事件加入UIApplication管理的事件队列中去,这个队列是先进先出的,然后UIApplication会从事件队列中去取出最前面的事件,并将事件分发下去以便处理:
    以我们这个例子来看,当我点击蓝色view,他的事件传递从最上层的UIAplication逐次向下传递,我们通过下面的代码来模拟:
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        [super touchesBegan:touches withEvent:event];
    }
    
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
        NSLog(@"查找到了%@",NSStringFromClass([self class]));
        // 1.判断下自己能否接收事件
        if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
        // 2.判断下点在不在当前控件上
        if ([self pointInside:point withEvent:event] == NO) return  nil; // 点不在当前控件
        int count = (int)self.subviews.count;
        for (int i = count - 1; i >= 0; i--) {
            UIView *childView = self.subviews[i];
            // 把当前坐标系上的点转换成子控件上的点
            CGPoint childP =  [self convertPoint:point toView:childView];
            UIView *actionView = [childView hitTest:childP withEvent:event];
            if (actionView) {
                return actionView;
            }
        }
        // 4.如果没有比自己合适的子控件,最合适的view就是自己
        return self;
    }
    
    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
        return CGRectContainsPoint(self.bounds, point);
    }
    

    其实系统会逐次递归调用以上的两个方法,直至找到最合适的view

    (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    

    上面这个方法判断响应事件的point是否包含在自己的frame内部

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

    以上这个方法来查找最合适的处理的view,


    event distribute desc.png

    断点打印如下:


    example.png

    找到最合适的view(即蓝色的view)以后,这个view会将Touch和Event向上传递:
    我们可以通过模拟调用下面这个方法来模拟:

    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        [super touchesBegan:touches withEvent:event];
    }
    

    断点截图如下:


    event distribute.png breakPoint stack.png

    最终事件回到了最上层的UIApplication,在APPDelegate里重写touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event ,我们会发现断点走到这里来了:


    event.png

    当然了,iOS系统后面做的一些处理,我们可以在clang命令里面查看C ++源码(这些操作是很复杂的,这里我个人能力有限,请大家自行去研究,菜逼如我是不能给大家分析的),这里简单的模拟下这个过程,意在让读者理解事件传递和响应的顺序和过程,结合前面我写的那片基础点的文章来看, 希望对您理解事件传递和响应有所帮助,写的不对的地方希望读者不吝赐教,如果您觉着对您有帮助,左下角赞我一波啊!🤗

    相关文章

      网友评论

          本文标题:iOS事件传递+响应者链条(responseChain)模拟

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