iOS文件補完計劃--UIGestureRecognizer

ofollow,noindex">目錄
- 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
,因此 View
的 touchesMoved:withEvent:
在開始一段時間內會持續呼叫;
當手勢識別器成功識別了該滑動手勢時,手勢識別器的action開始呼叫,同時通知Application取消 View
對事件的響應。之後僅由滑動手勢識別器接收事件並響應, View
不再接收事件。
(其實原理都是一樣的、只是持續性手勢需要一個識別的過程而已)
這裡有幾個點可以說說:
- 可以看到右側有一秒的時間差
也就是說View
的後續動作會等待GestureRecognizer
的識別結果。 - KTUITapGestureRecognizer以及KTUITapGestureRecognizer2的方法都被觸發了
也就是說是hit-tested
列表中所有View上的手勢識別器都會得到機會去識別事件。(依賴UITouch中的gestureRecognizers
屬性)
至於最後觸發誰、取決於代理中的設定。預設按照響應鏈的順序。
並且手勢在觸控事件處理流程中,處於觀察者的角色,其不是view層級結構的一部分,所以也不參與或者依賴responder chain(你把上層View的touch不呼叫super也影響不了)。
其流程大概如下圖所示:

注:圖中view與手勢的關係是,手勢關聯在view或view的superview(可能多級)上。
-
6個子類手勢
UIKit/Reference/UIGestureRecognizer_Class/Reference/Reference.html" target="_blank" rel="nofollow,noindex">UIGestureRecognizer 為一個抽象基類,定義了實現底層手勢識別行為的程式設計介面,你不應該直接使用他。
- UITapGestureRecognizer :用來識別點選手勢,包括單擊,雙擊,甚至三擊等。
- UIPinchGestureRecognizer :用來識別手指捏合手勢。
- UIPanGestureRecognizer :用來識別拖動手勢。
- UISwipeGestureRecognizer :用來識別Swipe手勢。
- UIRotationGestureRecognizer :用來識別旋轉手勢。
- UILongPressGestureRecognizer :用來識別長按手勢。
-
"離散手勢"和"連續手勢"
手勢分為離散型手勢(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:]
任意VIew tap.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
參考資料
iOS-UIGestureRecognizer詳解-原理篇
iOS觸控事件全家桶