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