Xcode 9.1
手势识别实际上是调用触摸事件来实现的。如果一个手势A的识别部分是另一个手势B的子部分时,默认情况下A就会先识别,B就无法识别了,造成手势冲突。例如拖动手势(UIPanGestureRecognizer)的操作事件是在手势的开始状态(UIGestureRecognizerStateBegan)开始执行的,而滑动手势(UISwipeGestureRecognizer)的操作事件只有在手势结束状态(UIGestureRecognizerStateEnded)才能执行,因此能识别拖动手势而不能识别滑动手势。
解决手势冲突办法:
使用requireGestureRecognizerToFail:方法,指定某个手势执行的前提是另一个手势失败后才会执行。
例如:
/* 解决手势冲突 */
// 解决 单击 与 双击 之间的冲突
[singleTap requireGestureRecognizerToFail:doubleTap];
// 解决 拖动 与 滑动 之间的冲突
[pan requireGestureRecognizerToFail:swipeToLeft];
[pan requireGestureRecognizerToFail:swipeToRight];
// 解决 拖动 和 长按 之间的冲突
[longPress requireGestureRecognizerToFail:pan];
和触摸事件一样,默认情况下,子视图(上层视图)触摸事件执行后就不再向父视图(下层视图)传递。如果想继续往下传递手势,可利用代理方法gestureRecognizer: shouldRecognizeSimultaneouslyWithGestureRecognizer:来实现。此代理方法默认返回NO,会阻断继续向下识别手势,如果返回YES则可以继续向下传递手势。
示例
注意UIImageView默认userInteractionEnabled为NO,这就无法识别手势。本例使用storyboard创建UIImageView并设置userInteractionEnabled为YES。
GestureVC.m
#import "GestureVC.h"
@interface GestureVC () <UIGestureRecognizerDelegate> {
NSInteger _currentIndex;
}
@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation GestureVC
- (void)viewDidLoad {
[super viewDidLoad];
/* ----- 点击手势 ----- */
// 默认单击
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapGesture:)];
[self.imageView addGestureRecognizer:singleTap];
// 双击
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapGesture:)];
doubleTap.numberOfTapsRequired = 2;
// 需要2根手指一起敲击
// tap.numberOfTouchesRequired = 2;
// 把点击手势添加到imageView上
[self.imageView addGestureRecognizer:doubleTap];
/* ----- 捏合手势 ----- */
// 模拟器触发:按住option键,再点鼠标左键
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchGesture:)];
[self.imageView addGestureRecognizer:pinch];
/* ----- 旋转手势 ----- */
// 模拟器触发:也是按住option键
UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationGesture:)];
[self.imageView addGestureRecognizer:rotation];
/* ----- 滑动手势 ----- */
// 默认往右滑
UISwipeGestureRecognizer *swipeToRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeToRightGesture:)];
[self.imageView addGestureRecognizer:swipeToRight];
// 改成往左滑
UISwipeGestureRecognizer *swipeToLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeToLeftGesture:)];
swipeToLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.imageView addGestureRecognizer:swipeToLeft];
// 支持两个方向(左+右/上+下),不同时支持横向和竖向,优先支持横向
// swipe.direction = UISwipeGestureRecognizerDirectionLeft|UISwipeGestureRecognizerDirectionRight;
/* ----- 拖动手势 ----- */
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
[self.imageView addGestureRecognizer:pan];
/* ----- 长按手势 ----- */
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGesture:)];
// 设置长按时间
longPress.minimumPressDuration = 1.0f;
[self.imageView addGestureRecognizer:longPress];
/* 解决手势冲突 */
// 解决 单击 与 双击 之间的冲突
[singleTap requireGestureRecognizerToFail:doubleTap];
// 解决 拖动 与 滑动 之间的冲突
[pan requireGestureRecognizerToFail:swipeToLeft];
[pan requireGestureRecognizerToFail:swipeToRight];
// 解决 拖动 和 长按 之间的冲突
[longPress requireGestureRecognizerToFail:pan];
}
#pragma mark - 手势识别响应方法
// 单击手势
- (void)singleTapGesture:(UITapGestureRecognizer *)gesture {
NSLog(@"singleTapGesture");
self.label.hidden = !self.label.hidden;
}
// 双击手势
- (void)doubleTapGesture:(UITapGestureRecognizer *)gesture {
NSLog(@"doubleTapGesture");
// 取消一切形变
self.imageView.transform = CGAffineTransformIdentity;
}
// 捏合手势
- (void)pinchGesture:(UIPinchGestureRecognizer *)gesture {
NSLog(@"pinchGesture");
if (gesture.state == UIGestureRecognizerStateChanged) { // 手势进行中
// 捏合手势中scale属性记录了缩放比例
self.imageView.transform = CGAffineTransformMakeScale(gesture.scale, gesture.scale);
}else if(gesture.state == UIGestureRecognizerStateEnded) { // 手势结束
}
}
// 旋转手势
- (void)rotationGesture:(UIRotationGestureRecognizer *)gesture {
NSLog(@"rotationGesture");
if (gesture.state == UIGestureRecognizerStateChanged) { // 手势进行中
// 旋转手势中rotation属性记录了旋转弧度
self.imageView.transform = CGAffineTransformMakeRotation(gesture.rotation);
}else if(gesture.state == UIGestureRecognizerStateEnded) { // 手势结束
}
}
// 向左滑动手势
- (void)swipeToLeftGesture:(UISwipeGestureRecognizer *)gesture {
NSLog(@"swipeToLeftGesture");
// 上一张图片
_currentIndex--;
if (_currentIndex < 0) {
_currentIndex = 2;
}else if (_currentIndex >2) {
_currentIndex = 0;
}
self.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",_currentIndex]];
}
// 向右滑动手势
- (void)swipeToRightGesture:(UISwipeGestureRecognizer *)gesture {
NSLog(@"swipeToRightGesture");
// 下一张图片
_currentIndex++;
if (_currentIndex < 0) {
_currentIndex = 2;
}else if (_currentIndex >2) {
_currentIndex = 0;
}
self.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",_currentIndex]];
}
// 拖动手势
- (void)panGesture:(UIPanGestureRecognizer *)gesture {
NSLog(@"panGesture");
if (gesture.state == UIGestureRecognizerStateChanged) { // 手势进行中
// 取得在相对根视图(self.view)的移动
CGPoint translation = [gesture translationInView:self.view];
// 改变imageView坐标
self.imageView.transform = CGAffineTransformMakeTranslation(translation.x, translation.y);
}else if(gesture.state == UIGestureRecognizerStateEnded){
// 0.5s后取消一切形变
[UIView animateWithDuration:0.5 animations:^{
self.imageView.transform = CGAffineTransformIdentity;
}];
}
}
// 长按手势
- (void)longPressGesture:(UILongPressGestureRecognizer *)gesture {
NSLog(@"longPressGesture");
// 此方法会调用两次(Began & Ended),需判断其手势状态
if (gesture.state == UIGestureRecognizerStateBegan) {
// 创建alertController
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"确定删除这张图片吗?" preferredStyle:UIAlertControllerStyleActionSheet];
// 添加确认按钮
[alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
// 这里啥也不做
}]];
// 添加取消按钮
[alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
// 这里啥也不做
}]];
// 显示alertController
[self presentViewController:alertController animated:YES completion:nil];
}
}
@end