iOS文檔補完計劃--UIGestureRecognizer

目錄

  • 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嵌溢,因此ViewtouchesMoved:withEvent:在開始一段時間內(nèi)會持續(xù)調(diào)用;
當手勢識別器成功識別了該滑動手勢時学少,手勢識別器的action開始調(diào)用秧骑,同時通知Application取消View對事件的響應。之后僅由滑動手勢識別器接收事件并響應乎折,View不再接收事件。
(其實原理都是一樣的忌堂、只是持續(xù)性手勢需要一個識別的過程而已)

這里有幾個點可以說說:

  1. 可以看到右側(cè)有一秒的時間差
    也就是說View的后續(xù)動作會等待GestureRecognizer的識別結(jié)果士修。
  2. 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

可見手勢的識別(KTUITapGestureRecognizertouchesBeagn)并不依賴響應鏈(ViewtouchesBeagn)笤昨、而是由[UIWindow sendEvent:]方法中被UIGestureEnvironment直接調(diào)起握恳。

  • 6個子類手勢

UIGestureRecognizer為一個抽象基類,定義了實現(xiàn)底層手勢識別行為的編程接口根竿,你不應該直接使用他。

  • "離散手勢"和"連續(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觸摸事件全家桶

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峡眶,一起剝皮案震驚了整個濱河市植锉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狮暑,老刑警劉巖暇赤,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鞋囊,居然都是意外死亡,警方通過查閱死者的電腦和手機译株,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乘寒,“玉大人,你說我怎么就攤上這事伞辛『蝗保” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵竿滨,是天一觀的道長捏境。 經(jīng)常有香客問我,道長垫言,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任鸠澈,我火速辦了婚禮截驮,結(jié)果婚禮上际度,老公的妹妹穿的比我還像新娘。我一直安慰自己乖菱,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布鹉勒。 她就那樣靜靜地躺著吵取,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上实辑,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天藻丢,我揣著相機與錄音,去河邊找鬼悠反。 笑死,一個胖子當著我的面吹牛萍摊,可吹牛的內(nèi)容都是我干的如叼。 我是一名探鬼主播冰木,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼死姚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逼龟,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤追葡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宜肉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡之斯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年佑刷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘫絮。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡填硕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栓辜,我是刑警寧澤垛孔,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站狭莱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏腋妙。R本人自食惡果不足惜讯榕,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愚屁。 院中可真熱鬧,春花似錦送浊、人聲如沸丘跌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至榆纽,卻和暖如春捏肢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸵赫。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工躏升, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓钻弄,卻偏偏與公主長得像者吁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子复凳,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345