一、父子视图
UIView *redView =[[UIView alloc]init];
[self.view addSubview:redView];
redView.backgroundColor = [UIColor redColor];
redView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap)];
[redView addGestureRecognizer:gesture];
UIView *yellowView =[[UIView alloc]init];
[redView addSubview:yellowView];
yellowView.backgroundColor = [UIColor yellowColor];
yellowView.frame = CGRectMake(100, 100, 100,100);
- (void)tap{
NSLog(@"点击了”);
}
- 父视图绑定了手势识别器,子视图因为重叠在父视图内,成为了父视图的一部分,这样点击子视图也会触发识别器的action
如果你想要点击黄色区域不会响应tap方法,而点击除了黄色区域外会响应tap方法,你可以这样
@interface ViewController ()<UIGestureRecognizerDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIView *redView =[[UIView alloc]init];
redView.tag=1;
[self.view addSubview:redView];
redView.backgroundColor = [UIColor redColor];
redView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap)];
[redView addGestureRecognizer:gesture];
gesture.delegate = self;
UIView *yellowView =[[UIView alloc]init];
yellowView.tag=2;
[redView addSubview:yellowView];
yellowView.backgroundColor = [UIColor yellowColor];
yellowView.frame = CGRectMake(100, 100, 100,100);
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if (touch.view.tag==2) {
return NO;
}
return YES;
}
- (void)tap{
NSLog(@"点击了”);
}
1.1、父子视图分别添加了相同的识别器
UIView *redView =[[UIView alloc]init];
[self.view addSubview:redView];
redView.backgroundColor = [UIColor redColor];
redView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap)];
[redView addGestureRecognizer:gesture];
UIView *yellowView =[[UIView alloc]init];
[redView addSubview:yellowView];
yellowView.backgroundColor = [UIColor yellowColor];
yellowView.frame = CGRectMake(100, 100, 100,100);
UITapGestureRecognizer *gesture1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap1)];
[yellowView addGestureRecognizer:gesture1];
- (void)tap{
NSLog(@"点击了”);
}
- (void)tap1{
NSLog(@"点击了”);
}
- redView绑定了自己的识别器gesture1(action=tap1),而redView的父视图yellowView 也绑定了识别器gesture(action=tap),按我们的道理看来说点击黄色的view,tap1和tap都会被响应。而事实上是只有tap1响应了,因为相似手势互斥的,只会响应一个手势,默认情况下优先级:自身的识别器>父视图的识别器,所以只有gesture1会识别成功。
1.2、调整识别器的优先级
-(void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
- 用法:[A requireGestureRecognizerToFail:B] 当A、B两个手势同时满足响应手势方法的条件时,B优先响应,A不响应。如果B不满足条件,A满足响应手势方法的条件,则A响应。其实这就是一个设置响应手势优先级的方法。
在1.1中我们知道,默认情况下优先级:自身的识别器>父视图的识别器。假如现在我们希望yellowView绑定的识别器失效,而会成功识别redView绑定的识别器,也就是调整默认的优先级。
[gesture1 requireGestureRecognizerToFail:gesture];
UIGestureRecognizerDelegate
gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
这个方法返回YES,第一个则失效
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
UIGestureRecognizerDelegate
gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
这个方法返回YES,第二个则失效
1.3、多个相似手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
- 是否支持多手势触发。默认NO。返回YES、表明支持多个手势识别器;说是多个其实就是两个,gestureRecognizer 代表的发送代理消息的那个gestureRecognizer 对象,而otherGestureRecognizer是另一个手势识别器对象
@interface ViewController ()<UIGestureRecognizerDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIView *redView =[[UIView alloc]init];
redView.tag=1;
[self.view addSubview:redView];
redView.backgroundColor = [UIColor redColor];
redView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap)];
[redView addGestureRecognizer:gesture];
UIView *yellowView =[[UIView alloc]init];
yellowView.tag=2;
[redView addSubview:yellowView];
yellowView.backgroundColor = [UIColor yellowColor];
yellowView.frame = CGRectMake(100, 100, 100,100);
UITapGestureRecognizer *gesture1 = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap1)];
[yellowView addGestureRecognizer:gesture1];
gesture1.delegate = self;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
- (void)tap{
NSLog(@"gestute”);
}
- (void)tap1{
NSLog(@"gestute1”);
}
通过这样我们就可以使1.1中互斥的手势同时被识别。
二、手势识别器和hit-tested view

Window在将事件传递给hit-tested view之前,会先将事件传递给相关的手势识别器并由手势识别器优先识别。若手势识别器成功识别了事件,就会取消hit-tested view对事件的响应;若手势识别器没能识别事件,hit-tested view才完全接手事件的响应权。
- 手势识别器比hit-tested view具有更高的事件响应优先级。
注意:没有绑定手势识别器的view暂且称之为hit-tested view,如此来区分
CoverView *coverView = [[CoverView alloc]init];
coverView.frame = CGRectMake(0,100 , 300, 300);
[self.view addSubview:coverView];
coverView.backgroundColor = [UIColor redColor];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:event:)];
[coverView addGestureRecognizer:gesture];
当你点击coverView时发生了点击事件,而coverView绑定的手势识别器将优先响应,hit-tested view 随之响应。当手势识别器成功识别了事件,就会取消hit-tested view对事件的响应;
2.1、 cancelsTouchesInView
这个属性可以控制当 UIGestureRecognizer 成功识别手势之后是否要取消响应链对触摸事件的响应,默认为 YES,设置为 NO 之后,即使 UIGestureRecognizer 识别了手势,UIGestureEnvironment 也不会发起对响应链的 cancel。
MyLongPressgesture *gesture = [[MyLongPressgesture alloc]initWithTarget:self action:@selector(test:)];
gesture.cancelsTouchesInView = NO;
[view addGestureRecognizer:gesture];
@implementation TestView
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
NSLog(@"手指正在TestView移动中:%s",__func__);
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesCancelled:touches withEvent:event];
NSLog(@"取消触摸事件:%s",__func__);
}

当设置cancelsTouchesInView为NO时,即使 UIGestureRecognizer 识别了手势,UIGestureEnvironment 也不会发起对响应链的 cancel。因此view触摸事件一直还存在,touchesMoved方法在手势响应期间也会执行。
当设置cancelsTouchesInView为YES时,在UIGestureRecognizer 成功识别了手势后,就会取消之前的触摸事件,touchesMoved方法肯定不会在执行了。
2.2、delaysTouchesBegan
设置为 YES 时,这个属性可以控制在 UIGestureRecognizer 识别手势期间截断事件,识别失败后响应链才能收到触摸事件,默认为NO。
MyLongPressgesture *gesture = [[MyLongPressgesture alloc]initWithTarget:self action:@selector(test:)];
gesture.delaysTouchesBegan = YES;
[view addGestureRecognizer:gesture];

根据打印可以知道,testView没有收到触摸事件。
如果再设置 gesture.cancelsTouchesInView = NO;会怎么样
MyLongPressgesture *gesture = [[MyLongPressgesture
alloc]initWithTarget:self action:@selector(test:)];
gesture.cancelsTouchesInView = NO;
gesture.delaysTouchesBegan = YES;
[view addGestureRecognizer:gesture];

测试结果发现,testView的touchBegin方法没有执行,但touchMove还是方法执行了。

根据文档的意思,触摸事件发生,视图会分析触摸阶段和移动阶段。当设置delaysTouchesBegan为YES,挂起的是TouchPhaseBegan 事件的传送,而UITouchPhaseMoved事件不会受到影响。
2.2、UIScrollViewDelayedTouchesBeganGestureRecognizer
UIScrollView中有一个私有的gesture,就是UIScrollViewDelayedTouchesBeganGestureRecognizer。这个手势识别器会截断hit-tested view事件并延迟0.15s才发送给hit-tested view。如果有手势识别器,事件会被gesture首先识别,0.15s内识别之后,就会取消hit-tested view事件,这样的情况下就不会给hit-tested view发送事件。
网友评论