美文网首页
ios 触摸事件总结

ios 触摸事件总结

作者: 沧海小鱼儿 | 来源:发表于2020-01-16 21:13 被阅读0次

    iOS里的触摸 UIResponder . UIGestureRecognizer

    一:UIResponder :

    触摸事件由触屏生成后如何传递到当前应用?

    1510019-62b6b1eec26730aa.png

    1,触摸响应核心方法,寻找 hit-tested view:

    • —(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    底层具体实现如下 :
    - (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;
    }
    

    — (Bool)pointInside:(CGPoint)point withEvent:(UIEvent *)event

    2, UIApplication将事件通过 sendEvent: 传递给事件所属的window,window同样通过 sendEvent: 再将事件传递给最佳响应者 hit-tested view,流程如下:

    UIApplication ——> UIWindow ——> hit-tested view
    

    3,响应链:

    每个响应者必定都是UIResponder对象

    UIResponder对象:

    • UIView
    • UIViewController
    • UIApplication
    • AppDelegate
    1510019-2a9a5bb5ed21bd43.png

    响应四个方法:

    //手指触碰屏幕,触摸开始
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
    //手指在屏幕上移动
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
    //手指离开屏幕,触摸结束
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
    //触摸结束前,某个系统事件中断了触摸,例如电话呼入
    - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
    

    通过4个响应触摸事件的方法来响应事件;,每个UIResponder对象默认都已经实现了这4个方法,但是默认不对事件做任何处理,单纯只是将事件沿着响应链传递。若要截获事件进行自定义的响应操作,就要重写相关的方法。

    
    UIView *a_subA = [[UIView alloc]initWithFrame:CGRectMake(10, 10, 50, 50)];
    a_subA.backgroundColor = UIColor.blueColor;
    [a addSubview:a_subA];
    
    上面只是单纯的添加view,无其他手势,只要在对应View里重写下方法该类的对象都可以让其相应对应触摸事件
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"YH_AViewtouchesEnded");
       [super touchesEnded:touches withEvent:event];
    }
    

    Hit-Testing过程中的事件拦截自定义举例:

    1, 不完整的事件,无法向下传递
    2,子视图超过父视图,点击子视图超出父视图部分响应;
    3,点A视图让B视图响应

    ……...

    二:UIGestureRecognizer 手势识别器

    问题1,初级:单独给AView 添加一个UITapGestureRecognizer 手势,Aview 还会调用那些自己的touch哪些方法?为什么会这样?

    看测试demo结果:

     - [AView touchesBegan:withEvent:]
     - [AView testGesture]
     - [AView touchesCancelled:withEvent:]
    

    会调用 [AView touchesBegan]、[AView touchesCancelled]方法;当touchesCancelled调用后, 就只有手势能接受了;

    注意点:

    手势识别器的tapAction的调用时机 ,并不是手势识别器接收到事件的时机,而是手势识别器成功识别事件后的时机,是手势识别器的状态变为UIGestureRecognizerStateRecognized;

    验证:自定义TestTapGestureRecognizer

     -[TestTapGestureRecognizer touchesBegan:withEvent:]
     -[AView touchesBegan:withEvent:]
     -[TestTapGestureRecognizer touchesEnded:withEvent:]
     -[AView tapGesture]
     -[AView touchesCancelled:withEvent:]
    

    问题2,

    单独给 FartherAView 添加一个UIPanGestureRecognizer 手势, 然后在FartherAView上添加一个Aview,点击Aview,此时 AView 和FartherAView会调用哪些touch和UIGestureRecognizer哪些方法?

    -[AView touchesBegan:withEvent:]
    -[FartherAView touchesBegan:withEvent:]
    
    /**调用tapGesture*/
    -[FartherAView tapGestureFartherA]
    
    -[AView touchesCancelled:withEvent:]
    -[FartherAView touchesCancelled:withEvent:]
    
    -[TestTapGestureRecognizer touchesBegan:withEvent:]
    -[AView touchesBegan:withEvent:]
    -[TestTapGestureRecognizer touchesEnded:withEvent:]
    -[FartherAView tapGestureFartherA]
    -[AView touchesCancelled:withEvent:]
    
    

    问题3:

    单独给 FartherAView 添加一个UIPanGestureRecognizer 手势, 然后在FartherAView上添加一个Aview,把Aview 的touch事件方法重写,不再继续传递touch事件, 点击Aview, 此时 UIPanGestureRecognizer 还能响应吗? AView 和FartherAView会调用哪些touch和UIGestureRecognizer哪些方法?

    -[AView touchesBegan:withEvent:]
    /**调用tapGesture*/
    -[FartherAView tapGestureFartherA]
    /**调用touchesCancelled*/
    -[AView touchesCancelled:withEvent:]
    

    验证版本:

    
    -[TestTapGestureRecognizer touchesBegan:withEvent:]
    -[AView touchesBegan:withEvent:]
    -[TestTapGestureRecognizer touchesEnded:withEvent:]
     -[FartherAView tapGestureFartherA]
    -[AView touchesCancelled:withEvent:]
    

    Window在将事件传递给hit-tested view之前,会先将事件传递给相关的手势识别器并由手势识别器优先识别。若手势识别器成功识别了事件,就会取消hit-tested view对事件的响应;若手势识别器没能识别事件,hit-tested view才完全接手事件的响应权;

    事件最先传递给了手势识别器,然后传递给了最佳响应者,在手势识别器识别成功手势后,调用最佳响应者的touchesCancelled:方法终止最佳响应者对于事件的响应。

    hook Window 的sendEvent方法;

    屏幕快照 2019-12-05 下午3.20.23.png

    总的调用先后顺序

    屏幕快照 2019-12-05 下午7.22.17.png

    总结:响应者链上 手势会被收集在一个数组里, 手势识别器比UIResponder具有更高的事件响应优先级!! 和UIResponder 的touch事件传递无关;

    cancelsTouchesInView:

    放开默认的取消touch事件,允许接受touch事件默认为YES。表示当手势识别器成功识别了手势之后,会通知Application取消响应链对事件的响应,并不再传递事件给hit-test view。若设置成NO,表示手势识别成功后不取消响应链对事件的响应,事件依旧会传递给hit-test view;

    delaysTouchesBegan

    delaysTouchesBegan = YES; 完全截断touch事件,不让touch调用一次

    手势识别器成功识别了手势,独吞了事件,不会再传递给YellowView。因此只打印了手势识别器成功识别手势后的action调用。

    允许多个手势同时识别共存

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
    

    是否允许手势响应

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
    

    案例应用:

    首页婴儿儿童浮层

    相关文章

      网友评论

          本文标题:ios 触摸事件总结

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