设为首页 加入收藏

TOP

事件传递和响应链(一)
2019-08-26 07:06:02 】 浏览:89
Tags:事件 传递 应链

前言

看关于这方面的文章基本没有能涉及到UIGestureRecognizers相关的文章,因此决定写这样一篇文章。也是我的第一篇文章,如有什么不对请及时指正。
本文主要通过一些实际测试来便于大家理解。

正文

事件传递和响应链流程图

  • IOKit.framework 为系统内核的库
  • SpringBoard.app 相当于手机的桌面
  • Source1 主要接收系统的消息
  • Source0 - UIApplication - UIWindow
  • 从window开始系统会调用hitTest:withEvent:pointInside来找到最优响应者,具体过程可参考下图
    hitTest
    • 比如我们在self.view 上依次添加view1、view2、view3(3个view是同级关系),那么系统用hitTest以及pointInside时会先从view3开始便利,如果pointInside返回YES就继续遍历view3的subviews(如果view3没有子视图,那么会返回view3),如果pointInside返回NO就开始遍历view2。反序遍历,最后一个添加的subview开始。也算是一种算法优化。后面会具体介绍hitTest的内部实现和具体使用场景。
  • UITouch会给gestureRecognizers和最优响应者也就是hitTestView发送消息
    • 默认view会走其touchBegan:withEvent:等方法,当gestureRecognizers找到识别的gestureRecognizer后,将会独自占有该touch,即会调用其他gestureRecognizer和hitTest view的touchCancelled:withEvent:方法,并且它们不再收到该touche事件,也就不会走响应链流程。下面会具体阐述UIContol和UIScrollView和其子类与手势之间的冲突和关系。
  • 当该事件响应完毕,主线程的Runloop开始睡眠,等待下一个事件。

1.hitTest:withEvent:和pointInside

1.1 hitTest:withEvent:和pointInside 演练

  • 测试hitTest和pointInside执行过程

    GSGrayView *grayView = [[GSGrayView alloc] initWithFrame:CGRectMake(0, kTopHeight, kScreenWidth/2, 400)];
    [self.view addSubview:grayView];
    
    GSRedView *redView = [[GSRedView alloc] initWithFrame:CGRectMake(0, 0, grayView.bounds.size.width / 2, grayView.bounds.size.height / 3)];
    [grayView addSubview:redView];
    
    GSBlueView *blueView = [[GSBlueView alloc] initWithFrame:CGRectMake(grayView.bounds.size.width/2, grayView.bounds.size.height * 2/3, grayView.bounds.size.width/2, grayView.bounds.size.height/3)];
    
    // blueView.userInteractionEnabled = NO;
    // blueView.hidden = YES;
    // blueView.alpha = 0.1;//0.0;
    [grayView addSubview:blueView];
    
    GSYellowView *yellowView = [[GSYellowView alloc] initWithFrame:CGRectMake(CGRectGetMinX(grayView.frame), CGRectGetMaxY(grayView.frame) + 20, grayView.bounds.size.width, 100)];
    [self.view addSubview:yellowView];

    hitTest测试

    点击redView:
    yellowView -> grayView -> blueView -> redView
  • 当点击redView时,因为yellowView和grayView同级,yellowView比grayView后添加,所以先打印yellowView,由于触摸点不在yellowView中因此打印grayView,然后遍历grayView的subViews分别打印blueView和redView。
  • 当hitTest返回nil时,也不会打印pointInside。因此可以得出pointInside是在hitTest后面执行的。
  • 当view的userInteractionEnabled为NO、hidden为YES或alpha<=0.1时,也不会打印pointInside方法。因此可以推断出在hitTest方法内部会判断如果这些条件一个成立则会返回nil,也不会调用pointInside方法。
  • 如果在grayView的hitTest返回[super hitTest:point event:event],则会执行gery.subviews的遍历(subviews 的 hitTest 与 pointInside),grayView的pointInside是判断触摸点是否在grayView的bounds内,grayView的hitTest是判断是否需要遍历他的subviews.
  • pointInside只是在执行hitTest时,会在hitTest内部调用的一个方法。也就是说pointInside是hitTest的辅助方法。
  • hitTest是一个递归函数

    1.2 hitTest:withEvent:内部实现代码还原

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    NSLog(@"-----%@",self.nextResponder.class);
    if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) return nil;
    //判断点在不在这个视图里
    if ([self pointInside:point withEvent:event]) {
        //在这个视图 遍历该视图的子视图
        for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
            //转换坐标到子视图
            CGPoint convertedPoint = [subview convertPoint:point fromView:self];
            //递归调用hitTest:withEvent继续判断
            UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
            if (hitTestView) {
                //在这里打印self.class可以看到递归返回的顺序。
                return hitTestView;
            }
        }
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Mixin Messenger 源码解读 1 — .. 下一篇考研辅助app的诞生!

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目