iOS手势处理
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 GestureView.h #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:selfaction:@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.fgreen:arc4random() % 255 / 255.fblue:arc4random() % 255 / 255.falpha:1.0f].CGColor; }@end GestureView.m- (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 GestureView.h #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:selfaction:@selector(gestureEvent:)];// 將手勢與區域綁定 [self addGestureRecognizer:_pinchGesture];}return self; }- (void)gestureEvent:(UIPinchGestureRecognizer *)sender {// self.transform = CGAffineTransformScale(self.transform, sender.scale, sender.scale);sender.scale = 1; }@end GestureView.m縮放手勢會用到矩陣變換.
?
UIRotationGestureRecognizer - 旋轉
GestureView.h + GestureView.m
#import <UIKit/UIKit.h>@interface GestureView : UIView@end GestureView.h #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:selfaction:@selector(gestureEvent:)];// 將手勢與區域綁定 [self addGestureRecognizer:_rotationGesture];}return self; }- (void)gestureEvent:(UIRotationGestureRecognizer *)sender {// 此處用到了矩陣變換self.transform = CGAffineTransformRotate(self.transform, sender.rotation);sender.rotation = 0; } GestureView.m?
UISwipeGestureRecognizer - 滑動
GestureView.h + GestureView.m
#import <UIKit/UIKit.h>@interface GestureView : UIView@end GestureView.h #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:selfaction:@selector(gestureEvent:)];_swipeGesture.direction = \UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;// 將手勢與區域綁定 [self addGestureRecognizer:_swipeGesture];}return self; }- (void)gestureEvent:(UISwipeGestureRecognizer *)sender {NSLog(@"left or right"); }@end GestureView.m?
UIPanGestureRecognizer - 平移
GestureView.h + GestureView.m
#import <UIKit/UIKit.h>@interface GestureView : UIView@end GestureView.h #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:selfaction:@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:CGPointZeroinView:self]; }@end GestureView.m?
UILongPressGestureRecognizer - 長按手勢
GestureView.h + GestureView.m
#import <UIKit/UIKit.h>@interface GestureView : UIView@end GestureView.h #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:selfaction:@selector(gestureEvent:)];_longPressGesture.minimumPressDuration = 2.0f;// 將手勢與區域綁定 [self addGestureRecognizer:_longPressGesture];}return self; }- (void)gestureEvent:(UILongPressGestureRecognizer *)sender {NSLog(@"觸發事件"); }@end GestureView.m?
?
問題:如何處理一個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:selfaction:@selector(gesture1Event:)];_tapGesture1.numberOfTapsRequired = 1;// 雙擊手勢_tapGesture2 = \[[UITapGestureRecognizer alloc] initWithTarget:selfaction:@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 GestureView.m實際上,這種方式會有延時感-_-!!!!
?
問題:如何將長按手勢和拖拽手勢合并在一起呢??
我們需要用代理實現,實現以下的方法:
- (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:selfaction:@selector(panEvent:)];[self addGestureRecognizer:_panGesture];_panGesture.delegate = self;_longPressGesture = \[[UILongPressGestureRecognizer alloc] initWithTarget:selfaction:@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:CGPointZeroinView: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 GestureView.m
根據手勢狀態來識別手勢觸發事件的全稱細節是十分重要的.
?
問題:如何讓一個view的部分區域響應拖拽事件呢?
比方說,我們只需要下面紅色線指定的區域響應拖拽事件:
#import "GestureView.h"@interface GestureView (){BOOL allowPan;}@property (nonatomic, strong) UIPanGestureRecognizer *panGesture; @end@implementation GestureView- (id)initWithFrame:(CGRect)frame {self = [super initWithFrame:frame];if (self){// 初始化時不允許拖拽allowPan = NO;_panGesture = \[[UIPanGestureRecognizer alloc] initWithTarget:selfaction:@selector(panEvent:)];[self addGestureRecognizer:_panGesture];}return self; }- (void)panEvent:(UIPanGestureRecognizer *)sender {// 獲取到當前手勢在當前視圖坐標中觸摸的點CGPoint point = [sender locationInView:self];// 手勢開始時置位(手勢事件開始過程中僅僅執行一回)if (sender.state == UIGestureRecognizerStateBegan){// 設定響應的區域if (self.bounds.size.height / 2.f >= point.x && self.bounds.size.width / 2.f >= point.y){allowPan = YES;}}// 手勢持續(手勢事件開始過程中執行多回)if (sender.state == UIGestureRecognizerStateChanged && allowPan == YES){// 移動的操作CGPoint translation = [sender translationInView:self];self.center = CGPointMake(self.center.x + translation.x,self.center.y + translation.y);[sender setTranslation:CGPointZeroinView:self];}// 手勢結束后置位(手勢事件開始過程中僅僅執行一回)if (sender.state == UIGestureRecognizerStateEnded){allowPan = NO;} }@end GestureView.m?要實現那個效果,以下方法是核心方法,配合手勢的狀態使用:
// 獲取到當前手勢在當前視圖坐標中觸摸的點
??? CGPoint point = [sender locationInView:self];
?
問題:如何在ViewController中獲取到點擊的坐標,讓一個view跟隨觸摸點移動呢?
可以使用這幾個最原始的處理觸摸事件的方法來達到效果.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
也可以直接使用拖拽手勢來實現的,不過不完美
#import "RootViewController.h"@interface RootViewController (){UIView *_panPoint;CALayer *_redLayer; }@end@implementation RootViewController- (void)viewDidLoad {[super viewDidLoad];// 初始化view_panPoint = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];_panPoint.layer.cornerRadius = 25.f;_panPoint.layer.masksToBounds = YES;[self.view addSubview:_panPoint];// 初始化一個layer_redLayer = [CALayer layer];_redLayer.frame = _panPoint.bounds;_redLayer.backgroundColor = [UIColor redColor].CGColor;_redLayer.opacity = 0.f;[_panPoint.layer addSublayer:_redLayer];// 定義手勢UIPanGestureRecognizer *panGesture = \[[UIPanGestureRecognizer alloc] initWithTarget:selfaction:@selector(panGestureEvent:)];[self.view addGestureRecognizer:panGesture]; }- (void)panGestureEvent:(UIPanGestureRecognizer *)sender {CGPoint touchPoint = [sender locationInView:self.view];if (sender.state == UIGestureRecognizerStateBegan){_panPoint.center = touchPoint;_redLayer.opacity = 1.0f;}else if (sender.state == UIGestureRecognizerStateChanged){_panPoint.center = touchPoint;}else if (sender.state == UIGestureRecognizerStateEnded){_panPoint.center = touchPoint;_redLayer.opacity = 0.0f;} }@end RootViewController.m他們兩者的對比關系:
?
?
手勢處理中核心的地方:
1.? UIGestureRecognizerState非常重要,觸發事件時可以直接根據這個狀態值來判斷事件的發生順序
2.? 處理多手勢沖突時,可以使用依賴requireGestureRecognizerToFail:來處理,但效果不好
3.? 處理多個手勢并發響應的時候,需要實現代理并執行方法,請參考上面的事例
4.? 僅僅處理一個view上局部的手勢事件,需要用到手勢的locationInView:方法,并與UIGestureRecognizerState狀態值配合使用
?
?
附錄:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
這是手勢的代理方法,在可以不移除手勢的情況下關閉手勢的響應,此方法涉及到響應鏈.
?
轉載于:https://www.cnblogs.com/YouXianMing/p/3715456.html
總結
- 上一篇: ###Fedora下安装Retext
- 下一篇: IIS 6.0 401 错误