UIGestureRecognizer 詳解

你真的了解UIGestureRecognizer嗎?

今天在封裝一個(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)微猖。

FJDoubleCheckView.gif

一. 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;
  }
}

UITapGestureRecognizerUIButton的點(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;
}

四. 最后

送上一張圖片:

向往.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胆数,隨后出現(xiàn)的幾起案子肌蜻,更是在濱河造成了極大的恐慌,老刑警劉巖必尼,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒋搜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡判莉,警方通過查閱死者的電腦和手機(jī)豆挽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來券盅,“玉大人帮哈,你說我怎么就攤上這事∶潭疲” “怎么了娘侍?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵咖刃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我憾筏,道長(zhǎng)嚎杨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任氧腰,我火速辦了婚禮枫浙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘容贝。我一直安慰自己自脯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布斤富。 她就那樣靜靜地躺著膏潮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪满力。 梳的紋絲不亂的頭發(fā)上焕参,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音油额,去河邊找鬼叠纷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛潦嘶,可吹牛的內(nèi)容都是我干的涩嚣。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼掂僵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼航厚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锰蓬,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤幔睬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后芹扭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麻顶,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年舱卡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辅肾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轮锥,死狀恐怖宛瞄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤份汗,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站蝴簇,受9級(jí)特大地震影響杯活,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熬词,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一旁钧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧互拾,春花似錦歪今、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骑疆,卻和暖如春田篇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箍铭。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工泊柬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诈火。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓兽赁,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親冷守。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刀崖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容