美文网首页
iOS事件与响应

iOS事件与响应

作者: 你weixiao的时候很美 | 来源:发表于2019-01-09 16:49 被阅读4次

    参考文件:
    1.官方文档
    2.iOS触摸事件那点儿事
    3.iOS 点击事件传递及响应
    4.iOS事件处理,看我就够了

    1. iOS中的事件介绍:

    iOS中的事件分为3大类: 触屏事件(点击,拖动,手势等), 传感器事件(摇动,旋转方向等), 远程遥控事件(耳机线控等)。

    触屏事件分为2种:

    • 高级事件处理:UIControl类事件 和 手势识别器事件。
    • 低级事件处理:cell点击等其他触屏事件。
    2. 低级事件处理:
    1. 先介绍几个类:
    UITouch

    当一根手指触摸屏幕,会创建出一个UITouch对象。 可以根据NSSet中UITouch的对象数量判断此次触摸时间是单指触摸还是多指。

    UIEvent

    每产生一个时间,就对应一个UIEvent,UIEvent记录
    事件产生的类型,时间等。

    UIResponse

    在iOS中不是任何对象都能处理事件,只有继承了UIResponse才能接收并处理事件,称为响应者。

    响应者对象提供一下方法可以处理事件:

    触摸事件
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
    
    传感器事件
    - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
    - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
    
    远程控制事件
    - (void)remoteControlReceivedWithEvent:(UIEvent *)event;
    

    2.事件的传递:
    发送触摸时间后,系统会将该事件加入到一个由UIApplication管理的队列中,UIApplication会取出最前面的事件,发送给应用程序的主窗口(keywindow)。 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,即 事件的传递过程。

    重点是如何找到最合适响应事件的view
    • 首先判断自己是否能接受触摸事件。
    • 触摸点是否在自己身上。
    • 遍历子控件,重复前边2个步骤。
    • 如果没有符合条件的子控件,那么就认为自己最合适。

    UIView不能接受实践的3种情况:

    • 不接受用户交互,userInteractionEnabled = NO;
    • 隐藏, hidden = YES;
    • 透明: alpha = 0.0 -0.01

    判断触摸点是否在自己身上的方法是:pointInside:withEvent

    //  如果触摸点在自己的范围,返回YES,否则返回NO。
     - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    

    寻找最合适的view的方法是:hitTest:withEvent

    底层具体实现如下 :
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
        // 1.判断当前控件能否接收事件
        if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
        // 2. 判断点在不在当前控件
        if ([self pointInside:point withEvent:event] == NO) return nil;
        // 3.从后往前遍历自己的子控件
        NSInteger count = self.subviews.count;
        for (NSInteger i = count - 1; i >= 0; i--) {
            UIView *childView = self.subviews[I];
            // 把当前控件上的坐标系转换成子控件上的坐标系
            CGPoint childP = [self convertPoint:point toView:childView];
           // 调用递归方法
            UIView *fitView = [childView hitTest:childP withEvent:event];
            if (fitView) { 
               // 寻找到最合适的view则返回。
                return fitView;
            }
        }
        // 循环结束,表示没有比自己更合适的view
        return self;
    }
    

    3.事件的响应
    首先介绍一下响应链:


    响应者链.png

    -找到最合适的view后,会调用touches方法处理事件。
    -touches的默认默认做法是将事件顺着响应链向上传递,即调用super 的touches方法。

    //只要点击控件,就会调用touchBegin,如果没有重写这个方法,自己处理不了触摸事件
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    // 默认会把事件传递给上一个响应者
    [super touchesBegan:touches withEvent:event]; 
    // 注意不是调用父控件的touches方法,而是调用父类的touches方法
    }
    
    3. 高级事件处理:
    1. 手势识别事件

    Gesture recognizers receive touch and press events before their view does. If a view's gesture recognizers fail to recognize a sequence of touches, UIKit sends the touches to the view. If the view does not handle the touches, UIKit passes them up the responder chain.

    官方文档的话: 意思是手势识别器优先于view接收触摸事件。如果手势识别成功,取消事件在响应链的传递,如果手势识别失败,事件继续进行传递和响应。

    解释了为何在父view上有个手势,子视图tableview的cell点击不响应。

    2.UIControl的事件

    Controls communicate directly with their associated target object using action messages. When the user interacts with a control, the control sends an action message to its target object. Action messages are not events, but they may still take advantage of the responder chain. When the target object of a control is nil, UIKit starts from the target object and traverses the responder chain until it finds an object that implements the appropriate action method. For example, the UIKit editing menu uses this behavior to search for responder objects that implement methods with names like cut(_:), copy(_:), or paste(_:).

    这段官方文档的意思是: UIControl直接使用target-action的 消息来响应。消息不是事件,不通过Responder Chain派发,不会走响应者链。 有一种情况:如果消息没有target,那么,会走响应者链,比如 editing menu的 cut: copy:等。

    4. 整个事件和响应的逻辑

    1.当事件到来时,会通过hitTest和pointInside两个方法,从Window开始向上面的视图查找,找到第一响应者的视图。

    2.系统判断第一响应者如果是UIControl,直接用target-action来响应,不走响应者链。

    3.系统判断第一响应者是UIResponse,调动touchBegin:方法,但是不会调用touchEnd:方法。然后顺着响应链查找,如果在查找过程中,发现响应者链中有的视图添加了手势,则识别手势。如果识别出手势,则将第一响应者的事件取消,调用touchesCanceled:方法。由手势响应事件。

    4.如果手势识别失败,交给第一响应者来处理,按照响应者链逻辑处理事件。

    5.补充
    1. 史上最详细的iOS之事件的传递和响应机制

    相关文章

      网友评论

          本文标题:iOS事件与响应

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