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

2018-08-16  本文已影响0人  mrChan1234

在前面的文章中,我提到了事件传递和响应的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 ++源码(这些操作是很复杂的,这里我个人能力有限,请大家自行去研究,菜逼如我是不能给大家分析的),这里简单的模拟下这个过程,意在让读者理解事件传递和响应的顺序和过程,结合前面我写的那片基础点的文章来看, 希望对您理解事件传递和响应有所帮助,写的不对的地方希望读者不吝赐教,如果您觉着对您有帮助,左下角赞我一波啊!🤗

上一篇下一篇

猜你喜欢

热点阅读