iOS文檔補完計劃--UIGestureRecognizer

目錄

  • UIGestureRecognizerDelegate
  • 調節手勢識別
    • gestureRecognizerShouldBegin:
    • gestureRecognizer:shouldReceiveTouch:
  • 多手勢觸發
    • gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
    • gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
    • gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
  • UIGestureRecognizer
    • 手勢識別流程
    • 6個子類手勢
    • Action
  • 初始化
    • initWithTarget:action:
  • 添加和移除Target&&Action
    • addTarget:action:
    • removeTarget:action
  • 獲取手勢的觸摸和位置
    • locationInView:
    • locationOfTouch:inView:
    • numberOfTouches
  • 獲取手勢的狀態和View
    • state
    • view
    • enabled
  • 取消和延遲觸摸
    • cancelsTouchesInView
    • delaysTouchesBegan
    • delaysTouchesEnded
  • 添加依賴
    • requireGestureRecognizerToFail:

UIGestureRecognizerDelegate

你可以通過代理方法、去細緻的定製一些識別行爲

比如是否觸發手勢識別、是否進行手勢識別。多手勢衝突如何處理等

@protocol UIGestureRecognizerDelegate

調節手勢識別

  • - gestureRecognizerShouldBegin:

是否繼續進行手勢識別。默認YES

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;

返回NO則結束識別,不再觸發手勢。
可以在控件指定的位置開啓手勢識別

  • - gestureRecognizer:shouldReceiveTouch:

window對象在有觸摸事件發生時。默認YES

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
       shouldReceiveTouch:(UITouch *)touch;

位於touchesBegan:withEvent:之前被調用。
如果返回NO、該事件將不會被通知給GestureRecognizer


多手勢觸發

  • - gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:

是否支持多手勢觸發。默認NO。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

正常情況下只會有一個識別器進行手勢識別、也就是上層對象識別後則不再繼續傳播。
如果返回YES、響應者鏈上層對象觸發手勢識別後、如果下層對象也添加了手勢併成功識別也會繼續執行。

  • gestureRecognizer:shouldRequireFailureOfGestureRecognizer:

這個方法返回YES,第一個則失效

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

顧名思義吧、Failure Of GestureRecognizer

  • - gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:

這個方法返回YES,第二個手勢則失敗

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer 
shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

顧名思義吧、Fail By GestureRecognizer

需要注意這裏

些方法都有兩個UIGestureRecognizer參數、所以在一個對象的代理中返回並不一定能起到決定性作用
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:舉例:
只要任意一個返回YES、則這兩個就可以同時識別;
只有兩個都返回NO的時候、纔是互斥的。


UIGestureRecognizer

  • 手勢識別流程

大致理解是,Window在將事件傳遞給hit-tested view之前,會先將事件傳遞給相關的手勢識別器並由手勢識別器優先識別。若手勢識別器成功識別了事件,就會取消hit-tested view對事件的響應;若手勢識別器沒能識別事件,hit-tested view才完全接手事件的響應權。

佐證的話、你可以自定義一個子類並且重載一些方法、這裏直接貼結果
先用一個離散型手勢做實驗:

14:20:17-[KTUITapGestureRecognizer touchesBegan:withEvent:]
14:20:17-[KTUITapGestureRecognizer2 touchesBegan:withEvent:]
14:20:17-[View2 touchesBegan:withEvent:]
14:20:17-[View touchesBegan:withEvent:]
14:20:18-[KTUITapGestureRecognizer touchesEnded:withEvent:]
14:20:18-[KTUITapGestureRecognizer2 touchesEnded:withEvent:]
14:20:18-[View2 tapAction]
14:20:18-[View2 touchesCancelled:withEvent:]
14:20:18-[View touchesCancelled:withEvent:]
而對於持續型手勢

在一開始滑動的過程中,手勢識別器處在識別手勢階段,滑動產生的連續事件既會傳遞給手勢識別器又會傳遞給View,因此ViewtouchesMoved:withEvent:在開始一段時間內會持續調用;
當手勢識別器成功識別了該滑動手勢時,手勢識別器的action開始調用,同時通知Application取消View對事件的響應。之後僅由滑動手勢識別器接收事件並響應,View不再接收事件。
(其實原理都是一樣的、只是持續性手勢需要一個識別的過程而已)

這裏有幾個點可以說說:

  1. 可以看到右側有一秒的時間差
    也就是說View的後續動作會等待GestureRecognizer的識別結果。
  2. KTUITapGestureRecognizer以及KTUITapGestureRecognizer2的方法都被觸發了
    也就是說是hit-tested列表中所有View上的手勢識別器都會得到機會去識別事件。(依賴UITouch中的gestureRecognizers屬性)
    至於最後觸發誰、取決於代理中的設置。默認按照響應鏈的順序。

並且手勢在觸摸事件處理流程中,處於觀察者的角色,其不是view層級結構的一部分,所以也不參與或者依賴responder chain(你把上層View的touch不調用super也影響不了)。

其流程大概如下圖所示:

注:圖中view與手勢的關係是,手勢關聯在view或view的superview(可能多級)上。

  • 6個子類手勢

UIGestureRecognizer爲一個抽象基類,定義了實現底層手勢識別行爲的編程接口,你不應該直接使用他。

  • "離散手勢"和"連續手勢"

手勢分爲離散型手勢(discrete gestures)和持續型手勢(continuous gesture)

系統提供的離散型手勢包括點按手勢(UITapGestureRecognizer)和輕掃手勢(UISwipeGestureRecognizer),其餘均爲持續型手勢。

兩者主要區別在於狀態變化過程:

離散型:
識別成功:Possible —> Recognized
識別失敗:Possible —> Failed

持續型:
完整識別:Possible —> Began —> [Changed] —> Ended
不完整識別:Possible —> Began —> [Changed] —> Cancel

  • Action

最多含有1個參數、這與UIControl不同

- (void)handleGesture;
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;

你可以向gestureRecognizer詢問一些東西、比如可以通過調用locationInView:locationOfTouch:inView:來詢問手勢的位置。


初始化

  • - initWithTarget:action:

通過target&&action的形式初始化一個手勢識別器

- (instancetype)initWithTarget:(id)target 
                        action:(SEL)action;

添加和移除Target&&Action

  • - addTarget:action:

添加一對Target&&Action

- (void)addTarget:(id)target 
           action:(SEL)action;

與UIControl的機制一樣、Target&&Action成對作爲標識、再次添加無效。

  • - removeTarget:action

移除一對Target&&Action

- (void)removeTarget:(id)target 
              action:(SEL)action;

傳遞nil則匹配所有動作:
Target=nil則刪除所有
Action=nil則刪除該Target所有


獲取手勢的觸摸和位置

  • - locationInView:

獲取手勢觸摸的位置

- (CGPoint)locationInView:(UIView *)view;

如果傳入nil則指定爲window

比如我們可以設定允許判定手勢的rect

//設置點擊的範圍
- (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;
  }
}
  • - locationOfTouch:inView:

多點觸摸時、查找指定touch的poinit

- (CGPoint)locationOfTouch:(NSUInteger)touchIndex 
                    inView:(UIView *)view;

touchIndex代表指定touch的索引
view傳入nil則指定爲window

  • numberOfTouches

該手勢所獲取到的總觸摸點數

@property(nonatomic, readonly) NSUInteger numberOfTouches;

獲取手勢的狀態和View

  • state

手勢識別器當前的識別狀態

@property(nonatomic, readwrite) UIGestureRecognizerState state;

UIGestureRecognizerState是一個枚舉類型

typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
    //尚未識別是何種手勢操作(但可能已經觸發了觸摸事件),默認狀態
    UIGestureRecognizerStatePossible,   
    //手勢已經開始,此時已經被識別,但是這個過程中可能發生變化,手勢操作尚未完成
    UIGestureRecognizerStateBegan,     
    //手勢狀態發生改變
    UIGestureRecognizerStateChanged, 
    // 手勢識別操作完成(此時已經鬆開手指)  
    UIGestureRecognizerStateEnded, 
    //手勢被取消,恢復到默認狀態   
    UIGestureRecognizerStateCancelled, 
    //手勢識別失敗,恢復到默認狀態
    UIGestureRecognizerStateFailed,    
    //手勢識別完成,同end
    UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded 
};

手勢分爲離散型手勢(discrete gestures)和持續型手勢(continuous gesture)
二者可能經歷的狀態不一樣。

具體可以返回去看看《"離散手勢"和"連續手勢"》那一塊的說明。

  • view

手勢所添加到的視圖

@property(nonatomic, readonly) UIView *view;
  • enabled

手勢識別器是否開啓。默認YES

@property(nonatomic, getter=isEnabled) BOOL enabled;

如果在手勢識別器正在識別手勢時將此屬性更改爲NO,則手勢識別器將轉換爲已取消狀態。


取消和延遲觸摸

  • cancelsTouchesInView

識別成功後--是否向View發送cancel消息。默認YES

@property(nonatomic) BOOL cancelsTouchesInView;

若設置成NO,表示手勢識別成功後不取消響應鏈對事件的響應,事件依舊會傳遞給hit-test view。

  • delaysTouchesBegan

手勢識別器在識別手勢期間,是否截斷事件,即不會將事件發送給hit-tested view。默認爲NO。

@property(nonatomic) BOOL delaysTouchesBegan;

正常情況

14:20:17-[KTUITapGestureRecognizer touchesBegan:withEvent:]
14:20:17-[KTUITapGestureRecognizer2 touchesBegan:withEvent:]
14:20:17-[View2 touchesBegan:withEvent:]
14:20:17-[View touchesBegan:withEvent:]
14:20:18-[KTUITapGestureRecognizer touchesEnded:withEvent:]
14:20:18-[KTUITapGestureRecognizer2 touchesEnded:withEvent:]
14:20:18-[View2 tapAction]
14:20:18-[View2 touchesCancelled:withEvent:]
14:20:18-[View touchesCancelled:withEvent:]

任意VIewtap.delaysTouchesBegan = YES;之後

14:55:37-[KTUITapGestureRecognizer touchesBegan:withEvent:]
14:55:37-[KTUITapGestureRecognizer2 touchesBegan:withEvent:]
14:55:37-[KTUITapGestureRecognizer touchesEnded:withEvent:]
14:55:37-[KTUITapGestureRecognizer2 touchesEnded:withEvent:]
14:55:37-[View2 tapAction]

可以看出來多個手勢只要有一個設置爲YES、整條響應鏈上的VIew都不會收到消息。

  • delaysTouchesEnded

當手勢識別失敗時,若此時觸摸已經結束,是否延遲調用響應者的 touchesEnded:withEvent。默認YES

@property(nonatomic) BOOL delaysTouchesEnded;

若設置成NO,則在手勢識別失敗時會立即通知Application發送狀態爲end的touch事件給hit-tested view以調用 touchesEnded:withEvent: 結束事件響應。
若設置成YES,會延遲大概0.15ms,期間沒有接收到別的touch纔會發送touchesEnded。


添加依賴

  • requireGestureRecognizerToFail:

只有當另一個手勢識別失敗時纔會除非本手勢

- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;

[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
這樣即使singleTapGesture已經識別成功、也會等到doubleTapGesture識別失敗再觸發自身Action


參考資料

官方文檔-UIGestureRecognizer
你真的瞭解UIGestureRecognizer嗎?
iOS-UIGestureRecognizer詳解-原理篇
UIGestureRecognizer學習筆記
iOS觸摸事件全家桶

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章