iOS手势处理
iOS手势有着如下几种:
- UITapGestureRecognizer
- UIPinchGestureRecognizer
- UIRotationGestureRecognizer
- UISwipeGestureRecognizer
- UIPanGestureRecognizer
- UILongPressGestureRecognizer
上面的手势对应的操作是:
- Tap (点一下)
- Pinch
(二指往內或往外拨动,平时经常用到的缩放) 矩阵变换
- Rotation
(旋转)
矩阵变换
- Swipe (滑动,快速移动)
- Pan
(拖移,慢速移动)
矩阵变换
- LongPress (长按)
注意:以下示例均把手势封装进一个View当中
UITapGestureRecognizer - 点击手势
GestureView.h + GestureView.m
#import <UIKit/UIKit.h> @interface GestureView : UIView @end
#import "GestureView.h" @interface GestureView () @property (nonatomic, strong) UITapGestureRecognizer *tapGesture; @property (nonatomic, strong) CALayer *colorLayer; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化手势,给手势指定响应事件的对象 _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureEvent:)]; _colorLayer = [CALayer layer]; _colorLayer.frame = self.bounds; [self.layer addSublayer:_colorLayer]; // 将手势与区域绑定 [self addGestureRecognizer:_tapGesture]; } return self; } - (void)gestureEvent:(UIGestureRecognizer *)sender { _colorLayer.backgroundColor = [UIColor colorWithRed:arc4random() % 255 / 255.f green:arc4random() % 255 / 255.f blue:arc4random() % 255 / 255.f alpha:1.0f].CGColor; } @end
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
Attaching a gesture recognizer to a view defines the scope of the represented gesture, causing it to receive touches hit-tested to that view and all of its subviews. The view establishes a strong reference to the gesture recognizer.
将手势识别器附着在一个view上,实际上定义了一个手势接收的区域,会将接收到的触摸事件传递给这个view以及这个view的说有的subviews.这个view会对这个手势识别器强引用.
可以总结两点:
1. 手势会传递给这个view中所有的subviews
2. view会强引用手势识别器
使用如下:
点击手势有两个参数可以设置:
numberOfTapsRequired 点击几次触发事件(默认是1)
numberOfTouchesRequired 需要几个手指点击(默认是1)
UIPinchGestureRecognizer - 缩放
GestureView.h + GestureView.m
#import <UIKit/UIKit.h> @interface GestureView : UIView @end
#import "GestureView.h" @interface GestureView () @property (nonatomic, strong) UIPinchGestureRecognizer *pinchGesture; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化手势,给手势指定响应事件的对象 _pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(gestureEvent:)]; // 将手势与区域绑定 [self addGestureRecognizer:_pinchGesture]; } return self; } - (void)gestureEvent:(UIPinchGestureRecognizer *)sender { // self.transform = CGAffineTransformScale(self.transform, sender.scale, sender.scale); sender.scale = 1; } @end
缩放手势会用到矩阵变换.
UIRotationGestureRecognizer - 旋转
GestureView.h + GestureView.m
#import <UIKit/UIKit.h> @interface GestureView : UIView @end
#import "GestureView.h" @interface GestureView () @property (nonatomic, strong) UIRotationGestureRecognizer *rotationGesture; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化手势,给手势指定响应事件的对象 _rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(gestureEvent:)]; // 将手势与区域绑定 [self addGestureRecognizer:_rotationGesture]; } return self; } - (void)gestureEvent:(UIRotationGestureRecognizer *)sender { // 此处用到了矩阵变换 self.transform = CGAffineTransformRotate(self.transform, sender.rotation); sender.rotation = 0; }
UISwipeGestureRecognizer - 滑动
GestureView.h + GestureView.m
#import <UIKit/UIKit.h> @interface GestureView : UIView @end
#import "GestureView.h" @interface GestureView () @property (nonatomic, strong) UISwipeGestureRecognizer *swipeGesture; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化手势,给手势指定响应事件的对象 _swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(gestureEvent:)]; _swipeGesture.direction = UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight; // 将手势与区域绑定 [self addGestureRecognizer:_swipeGesture]; } return self; } - (void)gestureEvent:(UISwipeGestureRecognizer *)sender { NSLog(@"left or right"); } @end
UIPanGestureRecognizer - 平移
GestureView.h + GestureView.m
#import <UIKit/UIKit.h> @interface GestureView : UIView @end
#import "GestureView.h" @interface GestureView () @property (nonatomic, strong) UIPanGestureRecognizer *panGesture; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化手势,给手势指定响应事件的对象 _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gestureEvent:)]; // 将手势与区域绑定 [self addGestureRecognizer:_panGesture]; } return self; } - (void)gestureEvent:(UIPanGestureRecognizer *)sender { // 此处用到了矩阵变换 CGPoint translation = [sender translationInView:self]; self.center = CGPointMake(self.center.x + translation.x, self.center.y + translation.y); [sender setTranslation:CGPointZero inView:self]; } @end
UILongPressGestureRecognizer - 长按手势
GestureView.h + GestureView.m
#import <UIKit/UIKit.h> @interface GestureView : UIView @end
#import "GestureView.h" @interface GestureView () @property (nonatomic, strong) UILongPressGestureRecognizer *longPressGesture; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化手势,给手势指定响应事件的对象 _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(gestureEvent:)]; _longPressGesture.minimumPressDuration = 2.0f; // 将手势与区域绑定 [self addGestureRecognizer:_longPressGesture]; } return self; } - (void)gestureEvent:(UILongPressGestureRecognizer *)sender { NSLog(@"触发事件"); } @end
问题:如何处理一个view中添加了两个手势,1个是单击的手势,一个是双击的手势呢?
可以使用这个方法requireGestureRecognizerToFail:
#import "GestureView.h" @interface GestureView () @property (nonatomic, strong) UITapGestureRecognizer *tapGesture1; @property (nonatomic, strong) UITapGestureRecognizer *tapGesture2; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 单击手势 _tapGesture1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gesture1Event:)]; _tapGesture1.numberOfTapsRequired = 1; // 双击手势 _tapGesture2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gesture2Event:)]; _tapGesture2.numberOfTapsRequired = 2; // 注意: 判断双击手势需要时间,也就是说会有延时 // 有事件触发时,先判断是不是 双击手势,如果不是就执行 单击手势 [_tapGesture1 requireGestureRecognizerToFail:_tapGesture2]; // 将手势与区域绑定 [self addGestureRecognizer:_tapGesture1]; [self addGestureRecognizer:_tapGesture2]; } return self; } - (void)gesture1Event:(UIGestureRecognizer *)sender { NSLog(@"1"); } - (void)gesture2Event:(UIGestureRecognizer *)sender { NSLog(@"2"); } @end
实际上,这种方式会有延时感-_-!!!!
问题:如何将长按手势和拖拽手势合并在一起呢?
我们需要用代理实现,实现以下的方法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
Asks the delegate if two gesture recognizers should be allowed to recognize gestures simultaneously.
询问这个代理,是否允许两个手势同时触发.
#import "GestureView.h" @interface GestureView ()<UIGestureRecognizerDelegate> { BOOL shouldAllowPan; } @property (nonatomic, strong) UIPanGestureRecognizer *panGesture; @property (nonatomic, strong) UILongPressGestureRecognizer *longPressGesture; @end @implementation GestureView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化时不允许拖拽 shouldAllowPan = NO; _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panEvent:)]; [self addGestureRecognizer:_panGesture]; _panGesture.delegate = self; _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressEvent:)]; _longPressGesture.minimumPressDuration = 1.0f; [self addGestureRecognizer:_longPressGesture]; _longPressGesture.delegate = self; } return self; } - (void)panEvent:(UIPanGestureRecognizer *)sender { if(shouldAllowPan == YES) { // 移动的操作 CGPoint translation = [sender translationInView:self]; self.center = CGPointMake(self.center.x + translation.x, self.center.y + translation.y); [sender setTranslation:CGPointZero inView:self]; } else if(sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled) { shouldAllowPan = NO; } } - (void)longPressEvent:(UIGestureRecognizer *)sender { // 长按开始 if(UIGestureRecognizerStateBegan == sender.state) { NSLog(@"长按开始"); self.backgroundColor = [UIColor redColor]; shouldAllowPan = NO; } // 长按进行中 if(UIGestureRecognizerStateChanged == sender.state) { NSLog(@"长按进行中"); shouldAllowPan = YES; } // 长按结束 if(UIGestureRecognizerStateEnded == sender.state) { NSLog(@"长按结束"); self.backgroundColor = [UIColor blackColor]; shouldAllowPan = NO; } } // 是否允许多个手势同时触发 - (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { // 允许 return YES; } // 是否允许继续跟踪触摸事件 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { // 条件满足的手势会被传递进来(如果是移动手势,) if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && shouldAllowPan == NO) { return NO; } return YES; } @end
根据手势状态来识别手势触发事件的全称细节是十分重要的.