UIGestureRecognizer 詳解

你真的了解UIGestureRecognizer嗎?

今天在封裝一個(gè)功能的時(shí)候,碰到了多個(gè)手勢(shì)之間的識(shí)別問(wèn)題,需要實(shí)現(xiàn)UIGestureRecognizerDelegate協(xié)議中的[gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]方法來(lái)實(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è)過(guò)程中可能發(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í)別的過(guò)程中(注意是識(shí)別過(guò)程),不會(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í)別到單擊無(wú)法識(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 {
    // 在原來(lái)的基礎(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 無(wú)限大
@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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容