目錄
- UIGestureRecognizerDelegate
-
調(diào)節(jié)手勢識別
- gestureRecognizerShouldBegin:
- gestureRecognizer:shouldReceiveTouch:
- 多手勢觸發(fā)
- gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
- gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
- gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
-
UIGestureRecognizer
- 手勢識別流程
- 6個子類手勢
- Action
-
初始化
- initWithTarget:action:
-
添加和移除Target&&Action
- addTarget:action:
- removeTarget:action
-
獲取手勢的觸摸和位置
- locationInView:
- locationOfTouch:inView:
- numberOfTouches
-
獲取手勢的狀態(tài)和View
- state
- view
- enabled
-
取消和延遲觸摸
- cancelsTouchesInView
- delaysTouchesBegan
- delaysTouchesEnded
-
添加依賴
- requireGestureRecognizerToFail:
UIGestureRecognizerDelegate
你可以通過代理方法、去細致的定制一些識別行為
比如是否觸發(fā)手勢識別塞耕、是否進行手勢識別嘴瓤。多手勢沖突如何處理等
@protocol UIGestureRecognizerDelegate
調(diào)節(jié)手勢識別
-
- gestureRecognizerShouldBegin:
是否繼續(xù)進行手勢識別。默認YES
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;
返回NO則結(jié)束識別廓脆,不再觸發(fā)手勢。
可以在控件指定的位置開啟手勢識別
-
- gestureRecognizer:shouldReceiveTouch:
window對象在有觸摸事件發(fā)生時驾讲。默認YES
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch;
位于touchesBegan:withEvent:
之前被調(diào)用席赂。
如果返回NO、該事件將不會被通知給GestureRecognizer
多手勢觸發(fā)
-
- gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
是否支持多手勢觸發(fā)谓晌。默認NO癞揉。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
正常情況下只會有一個識別器進行手勢識別溺欧、也就是上層對象識別后則不再繼續(xù)傳播毁靶。
如果返回YES逊移、響應者鏈上層對象觸發(fā)手勢識別后、如果下層對象也添加了手勢并成功識別也會繼續(xù)執(zhí)行胳泉。
-
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
參數(shù)笔诵、所以在一個對象的代理中返回并不一定能起到?jīng)Q定性作用
以gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
舉例:
只要任意一個返回YES姑子、則這兩個就可以同時識別;
只有兩個都返回NO的時候街佑、才是互斥的。
UIGestureRecognizer
-
手勢識別流程
大致理解是森逮,Window在將事件傳遞給hit-tested view之前磁携,會先將事件傳遞給相關(guān)的手勢識別器并由手勢識別器優(yōu)先識別。若手勢識別器成功識別了事件璃搜,就會取消hit-tested view對事件的響應鳞上;若手勢識別器沒能識別事件,hit-tested view才完全接手事件的響應權(quán)篙议。
佐證的話怠硼、你可以自定義一個子類并且重載一些方法移怯、這里直接貼結(jié)果
先用一個離散型手勢做實驗:
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:]
而對于持續(xù)型手勢
在一開始滑動的過程中舟误,手勢識別器處在識別手勢階段,滑動產(chǎn)生的連續(xù)事件既會傳遞給手勢識別器又會傳遞給View
嵌溢,因此View
的 touchesMoved:withEvent:
在開始一段時間內(nèi)會持續(xù)調(diào)用;
當手勢識別器成功識別了該滑動手勢時学少,手勢識別器的action開始調(diào)用秧骑,同時通知Application取消View
對事件的響應。之后僅由滑動手勢識別器接收事件并響應乎折,View
不再接收事件。
(其實原理都是一樣的忌堂、只是持續(xù)性手勢需要一個識別的過程而已)
這里有幾個點可以說說:
- 可以看到右側(cè)有一秒的時間差
也就是說View
的后續(xù)動作會等待GestureRecognizer
的識別結(jié)果士修。 - KTUITapGestureRecognizer以及KTUITapGestureRecognizer2的方法都被觸發(fā)了
也就是說是hit-tested
列表中所有View上的手勢識別器都會得到機會去識別事件樱衷。(依賴UITouch中的gestureRecognizers
屬性)
至于最后觸發(fā)誰、取決于代理中的設(shè)置沸移。默認按照響應鏈的順序桦山。
并且手勢在觸摸事件處理流程中醋旦,處于觀察者的角色捂人,其不是view層級結(jié)構(gòu)的一部分先慷,所以也不參與或者依賴responder chain(你把上層View的touch不調(diào)用super也影響不了)。
其流程大概如下圖所示:
注:圖中view與手勢的關(guān)系是,手勢關(guān)聯(lián)在view或view的superview(可能多級)上次泽。
-
手勢的識別并不依賴響應鏈
-
[KTUITapGestureRecognizer touchesBegan:withEvent:]
堆棧信息:
-[KTUITapGestureRecognizer touchesBegan:withEvent:](self=0x00006000003d6700, _cmd="touchesBegan:withEvent:", 0x0000600000dd1d40) ITapGestureRecognizer.m:14
-[UIGestureRecognizer _touchesBegan:withEvent:] + 240
-[UITouchesEvent _sendEventToGestureRecognizer:] + 287
-[UIGestureEnvironment _updateForEvent:window:]_block_invoke + 64
-[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 276
-[UIGestureEnvironment _updateForEvent:window:] + 200
-[UIWindow sendEvent:] + 4058
-[UIApplication sendEvent:] + 352
可見手勢的識別(KTUITapGestureRecognizer
的touchesBeagn
)并不依賴響應鏈(View
的touchesBeagn
)笤昨、而是由[UIWindow sendEvent:]
方法中被UIGestureEnvironment
直接調(diào)起握恳。
-
6個子類手勢
UIGestureRecognizer為一個抽象基類,定義了實現(xiàn)底層手勢識別行為的編程接口根竿,你不應該直接使用他。
- UITapGestureRecognizer:用來識別點擊手勢,包括單擊寇壳,雙擊,甚至三擊等泞歉。
- UIPinchGestureRecognizer:用來識別手指捏合手勢匿辩。
- UIPanGestureRecognizer:用來識別拖動手勢。
- UISwipeGestureRecognizer:用來識別Swipe手勢铲球。
- UIRotationGestureRecognizer:用來識別旋轉(zhuǎn)手勢。
- UILongPressGestureRecognizer:用來識別長按手勢选侨。
-
"離散手勢"和"連續(xù)手勢"
手勢分為離散型手勢(discrete gestures)和持續(xù)型手勢(continuous gesture)
系統(tǒng)提供的離散型手勢包括點按手勢(UITapGestureRecognizer
)和輕掃手勢(UISwipeGestureRecognizer
)然走,其余均為持續(xù)型手勢。
兩者主要區(qū)別在于狀態(tài)變化過程:
離散型:
識別成功:Possible —> Recognized
識別失敵柯亍:Possible —> Failed
持續(xù)型:
完整識別:Possible —> Began —> [Changed] —> Ended
不完整識別:Possible —> Began —> [Changed] —> Cancel
-
Action
最多含有1個參數(shù)拆檬、這與UIControl不同
- (void)handleGesture;
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;
你可以向gestureRecognizer
詢問一些東西、比如可以通過調(diào)用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
比如我們可以設(shè)定允許判定手勢的rect
//設(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;
}
}
-
- locationOfTouch:inView:
多點觸摸時选酗、查找指定touch的poinit
- (CGPoint)locationOfTouch:(NSUInteger)touchIndex
inView:(UIView *)view;
touchIndex
代表指定touch的索引
view
傳入nil則指定為window
-
numberOfTouches
該手勢所獲取到的總觸摸點數(shù)
@property(nonatomic, readonly) NSUInteger numberOfTouches;
獲取手勢的狀態(tài)和View
-
state
手勢識別器當前的識別狀態(tài)
@property(nonatomic, readwrite) UIGestureRecognizerState state;
UIGestureRecognizerState
是一個枚舉類型
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
};
手勢分為離散型手勢(discrete gestures)和持續(xù)型手勢(continuous gesture)
二者可能經(jīng)歷的狀態(tài)不一樣。
具體可以返回去看看《"離散手勢"和"連續(xù)手勢"》那一塊的說明剧罩。
-
view
手勢所添加到的視圖
@property(nonatomic, readonly) UIView *view;
-
enabled
手勢識別器是否開啟。默認YES
@property(nonatomic, getter=isEnabled) BOOL enabled;
如果在手勢識別器正在識別手勢時將此屬性更改為NO菱属,則手勢識別器將轉(zhuǎn)換為已取消狀態(tài)舰罚。
取消和延遲觸摸
-
cancelsTouchesInView
識別成功后--是否向View發(fā)送cancel消息薛耻。默認YES
@property(nonatomic) BOOL cancelsTouchesInView;
若設(shè)置成NO,表示手勢識別成功后不取消響應鏈對事件的響應饲漾,事件依舊會傳遞給hit-test view缕溉。
-
delaysTouchesBegan
手勢識別器在識別手勢期間,是否截斷事件证鸥,即不會將事件發(fā)送給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]
可以看出來多個手勢只要有一個設(shè)置為YES鸟蜡、整條響應鏈上的VIew都不會收到消息。
-
delaysTouchesEnded
當手勢識別失敗時跳座,若此時觸摸已經(jīng)結(jié)束,是否延遲調(diào)用響應者的
touchesEnded:withEvent
疲眷。默認YES
@property(nonatomic) BOOL delaysTouchesEnded;
若設(shè)置成NO,則在手勢識別失敗時會立即通知Application發(fā)送狀態(tài)為end的touch事件給hit-tested view以調(diào)用 touchesEnded:withEvent: 結(jié)束事件響應咪橙。
若設(shè)置成YES,會延遲大概0.15ms产舞,期間沒有接收到別的touch才會發(fā)送touchesEnded菠剩。
添加依賴
-
requireGestureRecognizerToFail:
只有當另一個手勢識別失敗時才會除非本手勢
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
這樣即使singleTapGesture
已經(jīng)識別成功、也會等到doubleTapGesture
識別失敗再觸發(fā)自身Action
最后
本文主要是自己的學習與總結(jié)准颓。如果文內(nèi)存在紕漏棺妓、萬望留言斧正。如果愿意補充以及不吝賜教小弟會更加感激怜跑。
參考資料
官方文檔-UIGestureRecognizer
你真的了解UIGestureRecognizer嗎?
iOS-UIGestureRecognizer詳解-原理篇
UIGestureRecognizer學習筆記
iOS觸摸事件全家桶