今天在封裝一個功能的時候,碰到了多個手勢之間的識別問題,需要實現(xiàn)UIGestureRecognizerDelegate
協(xié)議中的[gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]
方法來實現(xiàn)多個手勢識別器的共同識別。因此也借此整理下關(guān)于UIGestureRecognizer
相關(guān)知識點。
FJDoubleCheckView.gif
一. UIGestureRecognizer的定義
UIGestureRecognizer
是一個抽象基類,定義了實現(xiàn)底層手勢識別行為的編程接口,使得繼承它的子類能處理具體的手勢。
//當前手勢狀態(tài)
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
// 尚未識別是何種手勢操作(但可能已經(jīng)觸發(fā)了觸摸事件),默認狀態(tài)
UIGestureRecognizerStatePossible,
// 手勢已經(jīng)開始,此時已經(jīng)被識別,但是這個過程中可能發(fā)生變化,手勢操作尚未完成
UIGestureRecognizerStateBegan,
// 手勢狀態(tài)發(fā)生改變
UIGestureRecognizerStateChanged,
// 手勢識別操作完成(此時已經(jīng)松開手指)
UIGestureRecognizerStateEnded,
// 手勢被取消,恢復到默認狀態(tài)
UIGestureRecognizerStateCancelled,
// 手勢識別失敗,恢復到默認狀態(tài)
UIGestureRecognizerStateFailed,
// 手勢識別完成,同end
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
};
</br>
@interface UIGestureRecognizer : NSObject
//創(chuàng)建一個手勢對象并添加觸發(fā)事件
- (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action NS_DESIGNATED_INITIALIZER;
//給一個手勢對象添加監(jiān)聽事件
- (void)addTarget:(id)target action:(SEL)action;
//移除一個手勢的監(jiān)聽事件
- (void)removeTarget:(nullable id)target action:(nullable SEL)action;
//獲取當前手勢狀態(tài)
@property(nonatomic,readonly) UIGestureRecognizerState state;
//委托
@property(nullable,nonatomic,weak) id <UIGestureRecognizerDelegate> delegate;
//手勢識別是否可用
@property(nonatomic, getter=isEnabled) BOOL enabled;
//獲取手勢觸摸的View視圖 只讀
@property(nullable, nonatomic,readonly) UIView *view;
//是否取消觸摸控件的響應(yīng)
默認為YES,這種情況下當手勢識別器識別到觸摸之后,會發(fā)送touchesCancelled給觸摸到的控件以取消控件view對touch的響應(yīng),
這個時候只有手勢識別器響應(yīng)touch,當設(shè)置成NO時,手勢識別器識別到觸摸之后不會發(fā)送touchesCancelled給控件,
這個時候手勢識別器和控件view均響應(yīng)touch。
注意:手勢識別和觸摸事件是同時存在的,只是因為touchesCancelled導致觸摸事件失效、
@property(nonatomic) BOOL cancelsTouchesInView;
//是否延遲發(fā)送觸摸事件給觸摸到的控件
默認是NO,這種情況下當發(fā)生一個觸摸時,手勢識別器先捕捉到到觸摸,然后發(fā)給觸摸到的控件,兩者各自做出響應(yīng)。如果設(shè)置為YES,手勢識別器在識別的過程中(注意是識別過程),不會將觸摸發(fā)給觸摸到的控件,即控件不會有任何觸摸事件。只有在識別失敗之后才會將觸摸事件發(fā)給觸摸到的控件,這種情況下控件view的響應(yīng)會延遲約0.15ms。
@property(nonatomic) BOOL delaysTouchesBegan;
//如果觸摸識別失敗是否立即結(jié)束本次手勢識別的觸摸事件
@property(nonatomic) BOOL delaysTouchesEnded;
//指定一個手勢需要另一個手勢執(zhí)行失敗才會執(zhí)行,同時觸發(fā)多個手勢使用其中一個手勢的解決辦法
有時手勢是相關(guān)聯(lián)的,如單機和雙擊,點擊和長按,點下去瞬間可能只會識別到單擊無法識別其他,該方法可以指定某一個 手勢,即便自己已經(jīng)滿足條件了,也不會立刻觸發(fā),會等到該指定的手勢確定失敗之后才觸發(fā)
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
//獲取當前觸摸在指定視圖上的點
- (CGPoint)locationInView:(nullable UIView*)view;
//獲取觸摸手指數(shù)
- (NSUInteger)numberOfTouches;
//多指觸摸的觸摸點相對于指定視圖的位置
- (CGPoint)locationOfTouch:(NSUInteger)touchIndex inView:(nullable UIView*)view;
@end
二. 關(guān)于UIGestureRecognizerDelegate代理的內(nèi)容
@protocol UIGestureRecognizerDelegate <NSObject>
@optional
//開始進行手勢識別時調(diào)用的方法,返回NO則結(jié)束識別,不再觸發(fā)手勢,用處:可以在控件指定的位置使用手勢識別
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
//是否支持多手勢觸發(fā),返回YES,則可以多個手勢一起觸發(fā)方法,返回NO則為互斥
是否允許多個手勢識別器共同識別,一個控件的手勢識別后是否阻斷手勢識別繼續(xù)向下傳播,默認返回NO;
如果為YES,響應(yīng)者鏈上層對象觸發(fā)手勢識別后,如果下層對象也添加了手勢并成功識別也會繼續(xù)執(zhí)行,否則上層對象識別后則不再繼續(xù)傳播
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
// 這個方法返回YES,第一個手勢和第二個互斥時,第一個會失效
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
//這個方法返回YES,第一個和第二個互斥時,第二個會失效
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
//手指觸摸屏幕后回調(diào)的方法,返回NO則不再進行手勢識別,方法觸發(fā)等
此方法在window對象在有觸摸事件發(fā)生時,調(diào)用gesture recognizer的touchesBegan:withEvent:方法之前調(diào)用,
如果返回NO,則gesture recognizer不會看到此觸摸事件。(默認情況下為YES)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
@end
</br>
常用知識:
//是否同時支持多種手勢
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
//是否允許開始點擊
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
//設(shè)置點擊的范圍
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
//獲取當前的觸摸點
CGPoint curp = [touch locationInView:self.imageView];
if (curp.x <= self.imageView.bounds.size.width*0.5) {
return NO;
}else{
return YES;
}
}
UITapGestureRecognizer
和UIButton
的點擊事件沖突的解決辦法:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
if ([touch.view isKindOfClass:[UIButton class]]) {
return NO;
}
return YES;
}
三. UIGestureRecognizer的子類
UITapGestureRecognizer(輕觸,點按)
UILongPressGestureRecognizer(長按)
UISwipeGestureRecognizer(輕掃手勢)
UIRotationGestureRecognizer(旋轉(zhuǎn)手勢)
UIPanGestureRecognizer(拖拽手勢)
UIPinchGestureRecognizer(捏合手勢,縮放用)
1. UITapGestureRecognizer
(輕觸,點按)
@interface UITapGestureRecognizer : UIGestureRecognizer
//設(shè)置能識別到手勢的最少的輕觸次數(shù)(默認為1)
@property (nonatomic) NSUInteger numberOfTapsRequired;
//設(shè)置能識別到手勢的最少的手指的個數(shù)(默認為1)
@property (nonatomic) NSUInteger numberOfTouchesRequired;
@end
實例:
// 創(chuàng)建一個手勢對象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
// 設(shè)置能識別到手勢的最少的輕觸次數(shù)
tap.numberOfTapsRequired = 3;
// 設(shè)置能識別到手勢的最少的手指的個數(shù)
tap.numberOfTouchesRequired = 2;
//把手勢對象添加到對應(yīng)的控件中
[self.imgView addGestureRecognizer:tap];
2:UILongPressGestureRecognizer(長按手勢)
@interface UILongPressGestureRecognizer : UIGestureRecognizer
//設(shè)置能識別到手勢的最少的輕觸次數(shù)(默認為1)
@property (nonatomic) NSUInteger numberOfTapsRequired;
//設(shè)置能識別到手勢的最少的手指的個數(shù)(默認為1)
@property (nonatomic) NSUInteger numberOfTouchesRequired;
//設(shè)置能識別到長按手勢的最短的長按時間,單位:秒,默認為0.5
@property (nonatomic) CFTimeInterval minimumPressDuration;
//設(shè)置長按時允許移動的最大距離,單位:像素,默認為10像素
@property (nonatomic) CGFloat allowableMovement;
@end
實例:
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressAction:)];
// 設(shè)置能識別到長按手勢的最小的長按時間
longPress.minimumPressDuration = 0.5;
// "容錯的范圍"
longPress.allowableMovement = 10;
// 把長按手勢添加到對應(yīng)的控件中
[self.imgView addGestureRecognizer:longPress];
3:UISwipeGestureRecognizer(輕掃手勢)
typedef NS_OPTIONS(NSUInteger, UISwipeGestureRecognizerDirection) {
UISwipeGestureRecognizerDirectionRight = 1 << 0, //向右滑
UISwipeGestureRecognizerDirectionLeft = 1 << 1, //向左滑
UISwipeGestureRecognizerDirectionUp = 1 << 2, //向上滑
UISwipeGestureRecognizerDirectionDown = 1 << 3 //向下滑
};
@interface UISwipeGestureRecognizer : UIGestureRecognizer
//最少觸摸手指個數(shù),默認為1
@property(nonatomic) NSUInteger numberOfTouchesRequired;
//設(shè)置輕掃手勢支持的方向,默認為向右滑
@property(nonatomic) UISwipeGestureRecognizerDirection direction;
@end
實例
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.imgView addGestureRecognizer:swipeLeft];
4:UIRotationGestureRecognizer(旋轉(zhuǎn)手勢)
@interface UIRotationGestureRecognizer : UIGestureRecognizer
//旋轉(zhuǎn)的角度
@property (nonatomic) CGFloat rotation;
//旋轉(zhuǎn)速度,單位:度/秒、
@property (nonatomic,readonly) CGFloat velocity;
@end
實例:
//為圖片框添加一個旋轉(zhuǎn)手勢
UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotateAction:)];rotation.delegate = self;
[self.imgView addGestureRecognizer:rotation];
// 旋轉(zhuǎn)手勢的監(jiān)聽方法
- (void)rotateAction:(UIRotationGestureRecognizer *)recognizer {
// 在原來的基礎(chǔ)上, 累加多少度
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
// 每次旋轉(zhuǎn)完畢后將rotation的值, 恢復到0的位置.recognizer.rotation = 0;
}
5:UIPanGestureRecognizer(拖拽手勢)
@interface UIPanGestureRecognizer : UIGestureRecognizer
//設(shè)置觸發(fā)拖拽最少手指數(shù),默認為1
@property (nonatomic) NSUInteger minimumNumberOfTouches;
//設(shè)置觸發(fā)拖拽最多手指數(shù),默認為 UINT_MAX 無限大
@property (nonatomic) NSUInteger maximumNumberOfTouches;
//獲取當前拖拽位置
- (CGPoint)translationInView:(nullable UIView *)view;
//設(shè)置當前拖拽位置
- (void)setTranslation:(CGPoint)translation inView:(nullable UIView *)view;
//設(shè)置拖拽速度,單位:像素/秒
- (CGPoint)velocityInView:(nullable UIView *)view;
@end
實例
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
[self.imgView addGestureRecognizer:pan];
// 拖拽手勢的監(jiān)聽方法
- (void)panAction:(UIPanGestureRecognizer *)recognizer {
// 1. 獲取手指拖拽的時候, 平移的值
CGPoint translation = [recognizer translationInView:recognizer.view];
// 2. 讓當前控件做響應(yīng)的平移
recognizer.view.transform = CGAffineTransformTranslate(recognizer.view.transform, translation.x, translation.y);
// 3. 每次平移手勢識別完畢后, 讓平移的值不要累加
[recognizer setTranslation:CGPointZero inView:recognizer.view];
}
6:UIPinchGestureRecognizer(捏合手勢,縮放用)
@interface UIPinchGestureRecognizer : UIGestureRecognizer
//設(shè)置縮放比例
@property (nonatomic) CGFloat scale;
//獲取捏合速度,單位:縮放比/秒
@property (nonatomic,readonly) CGFloat velocity;
@end
實例
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)];
pinch.delegate = self;
[self.imgView addGestureRecognizer:pinch];
// 捏合手勢監(jiān)聽方法
- (void)pinchAction:(UIPinchGestureRecognizer *)recognizer {
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1.0;
}
四. 最后
送上一張圖片:
向往.jpg