UIGestureRecognizer 詳解

你真的了解UIGestureRecognizer嗎?

今天在封裝一個功能的時候,碰到了多個手勢之間的識別問題,需要實現(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;
  }
}

UITapGestureRecognizerUIButton的點擊事件沖突的解決辦法:

- (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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

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