美文网首页
2021-03-24 iOS手势问题

2021-03-24 iOS手势问题

作者: 我是小胡胡分胡 | 来源:发表于2021-03-24 12:13 被阅读0次

    View布局-subViews 层级布局
    1、touch 触摸手势 事件响应链-》hitTestView, 确定第一响应者
    2、在不同View上(父子View,平级View),确定第一响应者
    3、在同一个View上 ,自定义的UIControl/UIControl/UIControl系统子类(button,slider,segment等)target-action+ Gesture共存
    4、在同一个View上,多个Gesture共存
    5、UIView(touchesBegin)+UIControl+ Gesture 响应优先级
    6、多个Gesture在不同View上:
    父ScrollView+子View 手势
    父ScrollView+子ScrollView 手势
    父NavigationController View(侧滑手势)+子View手势

    UIResponder->UIView->UIControl

    1, UIResponder

    - (void)touchesXXXXX:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

    2, UIGestureRecognizer

    @interface UIView : UIResponder 
    @interface UIView (UIViewGestureRecognizers)
    
    @property(nullable, nonatomic,copy) NSArray<__kindof UIGestureRecognizer *> *gestureRecognizers NS_AVAILABLE_IOS(3_2);
    

    优先级最高响应,并忽略UIControl方法响应;

    直接通过UIApplication直接向其派发消息,并且不再向响应者链派发消息。
    [UIWindow sendEvent:] ---> [UIGestureEnvironment _updateGesturesForEvent:window:]--->

    @interface UIGestureRecognizer (UIGestureRecognizerProtected)
    
    // mirror of the touch-delivery methods on UIResponder
    // UIGestureRecognizers aren't in the responder chain, but observe touches hit-tested to their view and their view's subviews
    // UIGestureRecognizers receive touches before the view to which the touch was hit-tested
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
    - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
    - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;
    - (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches API_AVAILABLE(ios(9.1));
    

    state:possible/began/changed/ended/cancelled/failed
    enable
    cancelsTouchesInView
    delaysTouchesBegan
    delaysTouchesEnd
    allowedTouchTypes
    allowedPressTypes
    requiresExclusiveTouchType

    1. UIControl
      @interface UIControl : UIView

    [UIWindow sendEvent:] ---》 [UIWindow _sendTouchesForEvent:]

    会重写父类(UIResponder)的touchesXXXXX方法,可能会直接取消touchesCancelled/

    highlighted
    tracking
    touchInside
    selected
    enabled
    state:normal/highlighted/disabled/selected/focused/application/reserved

    touchesBegan-> beginTrackingWithTouch
    touchesEnded-> endTrackingWithTouch->target/action

    touchesBegan-> beginTrackingWithTouch
    touchesMoved-> continueTrackingWithTouch
    touchesMoved-> continueTrackingWithTouch
    touchesMoved-> continueTrackingWithTouch->pointinside
    touchesEnded->pointinside-> endTrackingWithTouch

    1, 寻找第一响应者

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
        //3种状态无法响应事件
         if (self.userInteractionEnabled == NO || self.hidden == YES ||  self.alpha <= 0.01) return nil; 
        //触摸点若不在当前视图上则无法响应事件
        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 *fitView = [childView hitTest:childP withEvent:event]; 
            if (fitView) 
            {
                //如果子视图中有更合适的就返回
                return fitView; 
            }
        } 
        //没有在子视图中找到更合适的响应视图,那么自身就是最合适的
        return self;
    }
    

    3, 事件是怎么传递的

    事件 是怎么接 收的??

    UIReponder 通过 TouchesXXXX方法接收 事件
    <UIKit/UIGestureRecognizerSubclass.h>

    UIGesture 也是 通过 TouchesXXXX方法接收 事件

    UIControl的 Tracking 系列方法是在 touch 系列方法内部调用的。比如 beginTrackingWithTouch 是在 touchesBegan 方法内部调用的

    Window怎么知道要把事件传递给哪些手势识别器?

    Application怎么知道要把event传递给哪个Window,以及Window怎么知道要把event传递给哪个hit-tested view的问题,
    手势识别器也是一样的,event绑定的touch对象上维护了一个手势识别器数组,里面的手势识别器毫无疑问是在hit-testing的过程中收集的

    UIControl会阻止父视图上的手势识别器行为,也就是UIControl处理事件的优先级比UIGestureRecognizer高,
    但前提是相比于父视图上的手势识别器。UIControl比其父视图上的手势识别器具有更高的事件响应优先级。

    如果UIControl本身添加了手势呢?测试结果:UIGesture执行。uicontroler的target-action不执行。对自定义UIControl,UIControl的系统子类(button,slider等)一样。

    准确地说只适用于系统提供的有默认action操作的UIControl,
    例如UIbutton、UISwitch等的单击,而对于自定义的UIControl,经验证,响应优先级比手势识别器低(父子view上,UIGesture的优先关系)

    Window先将事件传递给这些手势识别器,再传给hit-tested view。
    一旦有手势识别器成功识别了手势,Application就会取消hit-tested view对事件的响应。

    UIResponder响应触摸事件

    UIResponder、UIGestureRecognizer、UIControl,笼统地讲,事件响应优先级依次递增。这句话不对啊, 如果是父子view,uigesture最高。如果同一个view上,uibutton的target-action最高。自定义uicontrol低与uigesture

    如果多个UIGesture呢(同一个单View上的多个Gesture。父子View的各自的Gesutre)
    最后添加的有效

    问题1 , 父子view

    首先确定 hit- test View

    UIGestureRecognizer是默认往super view传递的
    1、 这个view没有添加gesture, 看他的父view有没有添加手势, 如果添加了, 父视图的手势方法响应
    2、 如果view添加了gesture, 父视图也添加了gesture, 子 view的优先响应, view的不满足条件激活条件的话, 父视图的响应

    UIResponder的, 也会往父视图传
    1、 view的touch执行了,
    2、 super view的touch执行了
    3、如果view重写toucheXXXX方法, 不调用super的toucheXXXX, 就在这个View终止了, 事件吸收了

    问题2 , 父子view/平级view/同一个view,即添加了UIGesture,又add了target-action,哪个方法执行?

    父子view,
    父view上有UIGesture,
    子view,如果是uibutton,uibutton的点击执行。
    子view,如果是UIControl或者自定义UIControl,父view的UIGesture方法执行。如果想要修改,百度一下UIGesture的cancelsTouchesInView是干嘛的,这里懒得bb了,反正我也不懂

    同一个view
    view上即添加UIGesture又add了target-action,
    无论view是UIButton/UIControl还是自定义UIControl子类,view上的UIGesture方法执行。

    平级view
    一个view在前面一个view在后面,但是他们是平级的。遵循响应链机制,hittest-view第一响应者view上的事件执行

    https://www.jianshu.com/p/df86508e2811

    测试代码:

    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        //[self testSameView];
        [self testSubview];
        //[self testneighborView];
    }
    
    - (void)testSameView {
        double x = 20;
        double y = 20;
        double width = 100;
        double height = 50;
    
        {
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(x, y, width, height);
            btn.backgroundColor = [UIColor redColor];
            [btn setTitle:@"btn" forState:UIControlStateNormal];
            [self.view addSubview:btn];
            y += 70;
    
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [btn addGestureRecognizer:tap];
        }
    
        {
            UIControl *btn = [UIControl new];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(x, y, width, height);
            btn.backgroundColor = [UIColor redColor];
            [self.view addSubview:btn];
            y += 70;
    
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [btn addGestureRecognizer:tap];
        }
    
        {
            CustomControl *btn = [CustomControl new];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(x, y, width, height);
            btn.backgroundColor = [UIColor redColor];
            [self.view addSubview:btn];
            y += 70;
    
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [btn addGestureRecognizer:tap];
        }
    }
    
    - (void)btnAction {
        NSLog(@"target-action");
    }
    
    - (void)tapAction {
        NSLog(@"tap called");
    }
    
    - (void)testSubview {
        double x = 20;
        double y = 20;
        double width = 100;
        double height = 50;
    
        {
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(0, 0, width, height);
            btn.backgroundColor = [UIColor redColor];
            [btn setTitle:@"btn" forState:UIControlStateNormal];
    
            UIView *view = [UIView new];
            view.frame = CGRectMake(x, y, width + 50, height + 50);
            view.backgroundColor = [UIColor grayColor];
            view.userInteractionEnabled = YES;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [view addGestureRecognizer:tap];
    
            [view addSubview:btn];
            [self.view addSubview:view];
    
            y += 120;
        }
    
        {
            UIControl *btn = [UIControl new];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(0, 0, width, height);
            btn.backgroundColor = [UIColor redColor];
            [self.view addSubview:btn];
    
            UIView *view = [UIView new];
            view.frame = CGRectMake(x, y, width + 50, height + 50);
            view.backgroundColor = [UIColor grayColor];
            view.userInteractionEnabled = YES;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [view addGestureRecognizer:tap];
    
            [view addSubview:btn];
            [self.view addSubview:view];
    
            y += 120;
        }
    
        {
            CustomControl *btn = [CustomControl new];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(0, 0, width, height);
            btn.backgroundColor = [UIColor redColor];
            [self.view addSubview:btn];
    
            UIView *view = [UIView new];
            view.frame = CGRectMake(x, y, width + 50, height + 50);
            view.backgroundColor = [UIColor grayColor];
            view.userInteractionEnabled = YES;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [view addGestureRecognizer:tap];
    
            [view addSubview:btn];
            [self.view addSubview:view];
    
            y += 120;
        }
    }
    
    - (void)testneighborView {
        double x = 20;
        double y = 20;
        double width = 100;
        double height = 50;
    
        {
            UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(x, y, width, height);
            btn.backgroundColor = [UIColor redColor];
            [btn setTitle:@"btn" forState:UIControlStateNormal];
    
            UIView *view = [UIView new];
            view.frame = CGRectMake(x, y, width + 50, height + 50);
            view.backgroundColor = [UIColor grayColor];
            view.userInteractionEnabled = YES;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [view addGestureRecognizer:tap];
    
            [self.view addSubview:view];
            [self.view addSubview:btn];
            y += 120;
        }
    
        {
            UIControl *btn = [UIControl new];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(x, y, width, height);
            btn.backgroundColor = [UIColor redColor];
            [self.view addSubview:btn];
    
            UIView *view = [UIView new];
            view.frame = CGRectMake(x, y, width + 50, height + 50);
            view.backgroundColor = [UIColor grayColor];
            view.userInteractionEnabled = YES;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [view addGestureRecognizer:tap];
    
            [self.view addSubview:view];
            [self.view addSubview:btn];
    
            y += 120;
        }
    
        {
            CustomControl *btn = [CustomControl new];
            [btn addTarget:self action:@selector(btnAction) forControlEvents:UIControlEventTouchUpInside];
            btn.frame = CGRectMake(x, y, width, height);
            btn.backgroundColor = [UIColor redColor];
            [self.view addSubview:btn];
    
            UIView *view = [UIView new];
            view.frame = CGRectMake(x, y, width + 50, height + 50);
            view.backgroundColor = [UIColor grayColor];
            view.userInteractionEnabled = YES;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
            [view addGestureRecognizer:tap];
    
            [self.view addSubview:view];
            [self.view addSubview:btn];
            y += 120;
        }
    }
    

    相关文章

      网友评论

          本文标题:2021-03-24 iOS手势问题

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