美文网首页
iOS-详解事件传递和响应者链

iOS-详解事件传递和响应者链

作者: 搬砖人666 | 来源:发表于2019-11-29 18:38 被阅读0次

    事件响应

    一、事件的分发和传递(确定事件的第一响应者):

    1.当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中
    2.UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。
    3.UIWindow将事件向下分发,即UIView。
    4.UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。
    5.遍历子控件,重复以上两步。
    6.如果没有找到,那么自己就是事件处理者。如果
    7.如果自己不能处理,那么不做任何处理。
    其中 UIView不接受事件处理的情况主要有以下三种
    1)alpha <0.01
    2)userInteractionEnabled = NO(中间一层userInteractionEnabled = NO,则事件响应者链受阻)
    3.hidden = YES.
    第4步中UIView如何判断触摸点在自己身上呢?

    -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
        return [super pointInside:point withEvent:event];
    }
    

    return YES,则点在自己身上,遍历子视图寻找下一层目标View,return NO,则此view和此view 的子view都不响应事件,不会成为事件的第一响应者。

    遍历子视图,寻找下一层目标view:
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
        // 1.如果控件不允许与用用户交互,那么返回nil
        if (self.userInteractionEnabled == NO || self.alpha <= 0.01 || self.hidden == YES){
            return nil;
        }
        // 2. 如果点击的点在不在当前控件中,返回nil
        if (![self pointInside:point withEvent:event]){
            return nil;
        }
        // 3.从后往前遍历每一个子控件
        for(int i = (int)self.subviews.count - 1 ; i >= 0 ;i--){
            // 3.1获取一个子控件
            UIView *childView = self.subviews[i];
            // 3.2当前触摸点的坐标转换为相对于子控件触摸点的坐标
            CGPoint childP = [self convertPoint:point toView:childView];
            // 3.3判断是否在在子控件中找到了更合适的子控件(递归循环)
            UIView *fitView = [childView hitTest:childP withEvent:event];
            // 3.4如果找到了就返回
            if (fitView) {
                return fitView;
            }
        }
        // 4.没找到,表示没有比自己更合适的view,返回自己
        return self;
    }
    

    子view会重复以上两个方法,最后的一个响应者即为第一响应者。

    二、确定响应者后进入事件处理流程:

    第一响应者无响应且无任何操作,则事件被废弃
    第一响应者响应事件,事件处理完成
    如下图点击屏幕中的view2,则
    AppDelegate --> window --> rootViewController --> contentView --> view2
    所组成的链条就是事件响应者链。
    整个过程详见下图1:
    注意一点:点击屏幕后不是点击了屏幕中的view,而仅仅是点击了屏幕,然后生成了一个event包含了点击坐标的信息,iOS系统会接收到点击事件交给当前前台的UIApplication继续往下处理,要是直接点击到view了还要什么响应者链

    视图层级 图1
    改变响应者链

    某个事件响应者链中View的pointInSide方法返回NO,则此view不响应事件,此view的上一响应者即为第一响应者。由下图2可看出响应链变小,此时点击view1、view2、contentView的第一响应者都是rootViewController。

    图2

    手动更改某个view的nextResponder也会改变事件响应者链,例如viewController的默认view的nextResponder指向了viewController,viewController上的View的点击事件全部ViewController中响应,类似图2中的contentView的点击事件全部在rootViewController(contentView的nextResponder)中响应

    欢迎留言,共同进步。

    参考资料:
    1、iOS-事件传递,响应者链条及常见面试题:https://www.jianshu.com/p/0892b08367cf
    2、iOS 响应者及响应者链:https://www.jianshu.com/p/89d20caabf41
    3、UIView的hitTest和pointInside方法:https://www.jianshu.com/p/c87de31b3985
    4、hitTest和pointInside方法你真的熟吗?:https://juejin.im/entry/59e07e616fb9a04528458048
    5、iOS事件响应链(通俗易懂篇):https://blog.csdn.net/u013602835/article/details/79986819

    相关文章

      网友评论

          本文标题:iOS-详解事件传递和响应者链

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