exclusiveTouch 属性拯救测试无厘头同时点击多个控件

https://www.jianshu.com/p/9140db4b6f76

背景是这样的:

我们的UI界面一般会在一个界面同时写很多的控件,并且同时可见,并且有很多的控件都会同时有点击事件。

举个栗子:

- (void)viewDidLoad {

   [superviewDidLoad];

   // Do any additional setup after loadingthe view, typically from a nib.

   

   // 创建按钮组

   for (NSInteger i= 0; i < 16; i++) {

       

        UIButton *btn = [UIButtonbuttonWithType:UIButtonTypeCustom];

        btn.tag = 100+i;

        btn.backgroundColor = [UIColorgreenColor];

        [btn addTarget:self action:@selector(clickBtn:)forControlEvents:UIControlEventTouchUpInside];

        btn.frame = CGRectMake(30+(i%4)*70, 100+(i/4)*70, 60, 60);

        [btn setTitle:[NSStringstringWithFormat:@"按钮%ld",i] forState:UIControlStateNormal];

        [self.view addSubview:btn];

   }

   

   

   // 创建可以点击的View

   UIView *touchView = [[UIView alloc] initWithFrame:CGRectMake(100, 400, 100, 100)];

   touchView.backgroundColor = [UIColor yellowColor];

   [self.viewaddSubview:touchView];

   UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickTap:)];

   [touchView addGestureRecognizer:tap];

   touchView.exclusiveTouch = YES;

   

}

 

#pragma mark- 手势控件点击事件

- (void)clickTap:(UITapGestureRecognizer *)sender

{

   NSLog(@"点击了  touchView");

   

}

 

#pragma mark- 按钮的点击事件

- (void)clickBtn:(UIButton *)sender

{

   NSLog(@"sender.tag ===== %ld",sender.tag);

}

繁杂响应事件UI图.png

按钮组的按钮多个是可以同时点击响应的,按钮组中的按钮和下面的touchView也是可以同时点击响应的。发挥你的想象在实际的工作中,如果类似这样的情况出现,同时响应多套逻辑,可能没有影响,也可能是很可怕的。

问题来了,怎么保证同时点击多个控件,只让首个被触发的控件相应呢?

为每个控件设置标识也不现实,并且响应如此之快。

这个时候可以考虑下exclusiveTouch属性了。

看一下苹果的相关解释:


相关解释.png

 

大致理解:如果设置为YES,会使得在同一时间同时点击的其他控件的响应事件受到阻塞。默认的情况下是NO。

更通俗的讲就是,有一个View的exclusiveTouch属性是YES,那么当有人点击他的时候,他就独霸了整个touch事件,再点击其他的控件不管用。在手离开这个View之前,之前点击其他的View是无效的。

只要将有机会同时点击的控件的exclusiveTouch属性都设置为YES的话,那么这个问题就愉快的解决了。

btn.exclusiveTouch =YES;

*** touchView.exclusiveTouch = YES;***

- (void)viewDidLoad {

   [superviewDidLoad];

   // Do any additional setup after loadingthe view, typically from a nib.

   

   // 创建按钮组

   for (NSInteger i= 0; i < 16; i++) {

       

        UIButton *btn = [UIButtonbuttonWithType:UIButtonTypeCustom];

        btn.tag = 100+i;

       btn.backgroundColor = [UIColor greenColor];

        [btn addTarget:self action:@selector(clickBtn:)forControlEvents:UIControlEventTouchUpInside];

        btn.frame = CGRectMake(30+(i%4)*70, 100+(i/4)*70, 60, 60);

        [btn setTitle:[NSString stringWithFormat:@"按钮%ld",i] forState:UIControlStateNormal];

        [self.view addSubview:btn];

        btn.exclusiveTouch = YES;

   }

   

   

   // 创建可以点击的View

   UIView *touchView = [[UIView alloc] initWithFrame:CGRectMake(100, 400, 100, 100)];

   touchView.backgroundColor = [UIColor yellowColor];

   [self.viewaddSubview:touchView];

   UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(clickTap:)];

   [touchView addGestureRecognizer:tap];

   touchView.exclusiveTouch = YES;

   

}

如有失误请各位路过大神即时指点,或有更好的做法,也请指点一二,在下感激不尽。



UIView 的exclusiveTouch属性

exclusiveTouch的意思是UIView会独占整个Touch事件,具体的来说,就是当设置了exclusiveTouch的 UIView是事件的第一响应者,那么到你的所有手指离开前,其他的视图UIview是不会响应任何触摸事件的,对于多点触摸事件,这个属性就非常重要,值得注意的是:手势识别(GestureRecognizers)会忽略此属性。

列举用途:我们知道ios是没有GridView视图的,通常做法是在UITableView的cell上加载几个子视图,来模拟实现 GridView视图,但对于每一个子视图来说,就需要使用exclusiveTouch,否则当同时点击多个子视图,那么会触发每个子视图的事件。当然 还有我们常说的模态对话框。


前言

iOS 中对手势响应事件的定义很丰富(参考iOS七种手势),最常用的是点击手势,若不做一些配置处理,你可能会遇到很尴尬的问题,如一个页面两个弹窗重叠,一个页面被Push了多次等。让我们一起探讨下,怎么避开这些坑。“同时”:物理世界中,事件的发生,并不存在绝对上的同时。

注意:我在下文描述时,用的是点击手势和响应区域,其实,UIView的子类都可以添加点击手势和响应事件,与点击UIbutton并响应事件本质上是相同的。

先抛出一些小结论:

1、点击事件之间是可以“同时”共存,并能分别“同时”响应的。(同时点击两个响应区域分别响应)

2、线程被阻塞时,点击手势也是可以缓存的,并在线程通畅后统一响应。(单个响应区域的重复点击并重复响应)。

发现问题

1、当一个页面中存在两个以上可点击的响应区域时,基本都存在“同时”点击,分别“同时”响应的问题,尤其是响应事件分别为弹出窗口或跳转到另一个页面时,窗口存在重叠效果,最为突出

2、当主线程被阻塞时,单个响应区域的重复点,会造成重复响应,如一个页面被Push了多次。

概述

  1. setExclusiveTouch  是UIView的一个属性,默认为NO(不互斥),设置UIView 接收手势的互斥性为YES,防止多个响应区域被“同时”点击,“同时”响应的问题。
  2. 可以通过  [[UIView appearance] setExclusiveTouch:YES]; UIImageView ,UILabel等,都可以添加手势,响应方式和UIButton 相同。全局设置响应区域的点击手势的互斥,是有效的。但使用此方法时,在iOS 8.0~iOS8.2(目前仅在该版本下发现问题)下会引起崩溃。

问题分析:

  1. UIView虽然遵守了UIAppearance 和 UIAppearanceContainer协议(支持UIAppearance协议的类可以访问appearance selector ,它为receiver返回appearance proxy,我么可以给proxy发一些消息),并能正常调用[[UIView appearance] setExclusiveTouch:YES];  (无警告,无报错),但 exclusiveTouch 并没有被声明UI_APPEARANCE_SELECTOR,低版本上可能是无效的,甚至会抛出 unknown selector 异常,引起崩溃。

解决方案:

  1. 方案一:setExclusiveTouch:YES 可以通过单独设置或批量设置UIView及其子类的手势互斥性,针对具体的使用场景,对个别的UIbutton等单独设置。这样可以解决主要的问题:点击两个不同的响应区域 出现两个弹窗或Push(present)页面的情况。但需要很多处设置,可能会有遗漏。
  2. 方案二:给UIView创建一个分类UIView+ExclusiveTouch,通过runtime 运行机制,给UIView 补充一个属性,如ygExclusiveTouch,遵守UIAppearance和UIAppearanceContainer协议,并对属性使用UI_APPEARANCE_SELECTOR 声明 ,并实现getter/setter方法,要在set方法中添加self.exclusiveTouch = ygExclusiveTouch;设置属性。

综述

  1. AppDelegatedidFinishLaunchingWithOptions方法最前面(创建视图之前)调用分类中的方法设置ygExclusiveTouch。可以实现全局设置点击区域的互斥性,是否能完全解决iOS 8.0~iOS8.2下会引起崩溃,需要验证。综合考虑,使用了方案一。后续抽时间,再验证完善这一块。
项目demo  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值