iOS文檔補(bǔ)完計(jì)劃--UIGestureRecognizer

目錄

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

UIGestureRecognizerDelegate

你可以通過(guò)代理方法、去細(xì)致的定制一些識(shí)別行為

比如是否觸發(fā)手勢(shì)識(shí)別、是否進(jìn)行手勢(shì)識(shí)別。多手勢(shì)沖突如何處理等

@protocol UIGestureRecognizerDelegate

調(diào)節(jié)手勢(shì)識(shí)別

  • - gestureRecognizerShouldBegin:

是否繼續(xù)進(jìn)行手勢(shì)識(shí)別。默認(rèn)YES

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

返回NO則結(jié)束識(shí)別,不再觸發(fā)手勢(shì)。
可以在控件指定的位置開啟手勢(shì)識(shí)別

  • - gestureRecognizer:shouldReceiveTouch:

window對(duì)象在有觸摸事件發(fā)生時(shí)。默認(rèn)YES

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

位于touchesBegan:withEvent:之前被調(diào)用。
如果返回NO、該事件將不會(huì)被通知給GestureRecognizer


多手勢(shì)觸發(fā)

  • - gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:

是否支持多手勢(shì)觸發(fā)。默認(rèn)NO。

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

正常情況下只會(huì)有一個(gè)識(shí)別器進(jìn)行手勢(shì)識(shí)別、也就是上層對(duì)象識(shí)別后則不再繼續(xù)傳播。
如果返回YES、響應(yīng)者鏈上層對(duì)象觸發(fā)手勢(shì)識(shí)別后、如果下層對(duì)象也添加了手勢(shì)并成功識(shí)別也會(huì)繼續(xù)執(zhí)行。

  • gestureRecognizer:shouldRequireFailureOfGestureRecognizer:

這個(gè)方法返回YES,第一個(gè)則失效

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

顧名思義吧、Failure Of GestureRecognizer

  • - gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:

這個(gè)方法返回YES,第二個(gè)手勢(shì)則失敗

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

顧名思義吧、Fail By GestureRecognizer

需要注意這里

些方法都有兩個(gè)UIGestureRecognizer參數(shù)、所以在一個(gè)對(duì)象的代理中返回并不一定能起到?jīng)Q定性作用
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:舉例:
只要任意一個(gè)返回YES、則這兩個(gè)就可以同時(shí)識(shí)別;
只有兩個(gè)都返回NO的時(shí)候、才是互斥的。


UIGestureRecognizer

  • 手勢(shì)識(shí)別流程

大致理解是,Window在將事件傳遞給hit-tested view之前,會(huì)先將事件傳遞給相關(guān)的手勢(shì)識(shí)別器并由手勢(shì)識(shí)別器優(yōu)先識(shí)別。若手勢(shì)識(shí)別器成功識(shí)別了事件,就會(huì)取消hit-tested view對(duì)事件的響應(yīng);若手勢(shì)識(shí)別器沒能識(shí)別事件,hit-tested view才完全接手事件的響應(yīng)權(quán)。

佐證的話、你可以自定義一個(gè)子類并且重載一些方法、這里直接貼結(jié)果
先用一個(gè)離散型手勢(shì)做實(shí)驗(yàn):

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:]
而對(duì)于持續(xù)型手勢(shì)

在一開始滑動(dòng)的過(guò)程中,手勢(shì)識(shí)別器處在識(shí)別手勢(shì)階段,滑動(dòng)產(chǎn)生的連續(xù)事件既會(huì)傳遞給手勢(shì)識(shí)別器又會(huì)傳遞給View,因此ViewtouchesMoved:withEvent:在開始一段時(shí)間內(nèi)會(huì)持續(xù)調(diào)用;
當(dāng)手勢(shì)識(shí)別器成功識(shí)別了該滑動(dòng)手勢(shì)時(shí),手勢(shì)識(shí)別器的action開始調(diào)用,同時(shí)通知Application取消View對(duì)事件的響應(yīng)。之后僅由滑動(dòng)手勢(shì)識(shí)別器接收事件并響應(yīng),View不再接收事件。
(其實(shí)原理都是一樣的、只是持續(xù)性手勢(shì)需要一個(gè)識(shí)別的過(guò)程而已)

這里有幾個(gè)點(diǎn)可以說(shuō)說(shuō):

  1. 可以看到右側(cè)有一秒的時(shí)間差
    也就是說(shuō)View的后續(xù)動(dòng)作會(huì)等待GestureRecognizer的識(shí)別結(jié)果。
  2. KTUITapGestureRecognizer以及KTUITapGestureRecognizer2的方法都被觸發(fā)了
    也就是說(shuō)是hit-tested列表中所有View上的手勢(shì)識(shí)別器都會(huì)得到機(jī)會(huì)去識(shí)別事件。(依賴UITouch中的gestureRecognizers屬性)
    至于最后觸發(fā)誰(shuí)、取決于代理中的設(shè)置。默認(rèn)按照響應(yīng)鏈的順序。

并且手勢(shì)在觸摸事件處理流程中,處于觀察者的角色,其不是view層級(jí)結(jié)構(gòu)的一部分,所以也不參與或者依賴responder chain(你把上層View的touch不調(diào)用super也影響不了)。

其流程大概如下圖所示:

注:圖中view與手勢(shì)的關(guān)系是,手勢(shì)關(guān)聯(lián)在view或view的superview(可能多級(jí))上。

  • 手勢(shì)的識(shí)別并不依賴響應(yīng)鏈
  • [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

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

  • 6個(gè)子類手勢(shì)

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

  • "離散手勢(shì)"和"連續(xù)手勢(shì)"

手勢(shì)分為離散型手勢(shì)(discrete gestures)和持續(xù)型手勢(shì)(continuous gesture)

系統(tǒng)提供的離散型手勢(shì)包括點(diǎn)按手勢(shì)(UITapGestureRecognizer)和輕掃手勢(shì)(UISwipeGestureRecognizer),其余均為持續(xù)型手勢(shì)。

兩者主要區(qū)別在于狀態(tài)變化過(guò)程:

離散型:
識(shí)別成功:Possible —> Recognized
識(shí)別失敗:Possible —> Failed

持續(xù)型:
完整識(shí)別:Possible —> Began —> [Changed] —> Ended
不完整識(shí)別:Possible —> Began —> [Changed] —> Cancel

  • Action

最多含有1個(gè)參數(shù)、這與UIControl不同

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

你可以向gestureRecognizer詢問一些東西、比如可以通過(guò)調(diào)用locationInView:locationOfTouch:inView:來(lái)詢問手勢(shì)的位置。


初始化

  • - initWithTarget:action:

通過(guò)target&&action的形式初始化一個(gè)手勢(shì)識(shí)別器

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

添加和移除Target&&Action

  • - addTarget:action:

添加一對(duì)Target&&Action

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

與UIControl的機(jī)制一樣、Target&&Action成對(duì)作為標(biāo)識(shí)、再次添加無(wú)效。

  • - removeTarget:action

移除一對(duì)Target&&Action

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

傳遞nil則匹配所有動(dòng)作:
Target=nil則刪除所有
Action=nil則刪除該Target所有


獲取手勢(shì)的觸摸和位置

  • - locationInView:

獲取手勢(shì)觸摸的位置

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

如果傳入nil則指定為window

比如我們可以設(shè)定允許判定手勢(shì)的rect

//設(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;
  }
}
  • - locationOfTouch:inView:

多點(diǎn)觸摸時(shí)、查找指定touch的poinit

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

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

  • numberOfTouches

該手勢(shì)所獲取到的總觸摸點(diǎn)數(shù)

@property(nonatomic, readonly) NSUInteger numberOfTouches;

獲取手勢(shì)的狀態(tài)和View

  • state

手勢(shì)識(shí)別器當(dāng)前的識(shí)別狀態(tài)

@property(nonatomic, readwrite) UIGestureRecognizerState state;

UIGestureRecognizerState是一個(gè)枚舉類型

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 
};

手勢(shì)分為離散型手勢(shì)(discrete gestures)和持續(xù)型手勢(shì)(continuous gesture)
二者可能經(jīng)歷的狀態(tài)不一樣。

具體可以返回去看看《"離散手勢(shì)"和"連續(xù)手勢(shì)"》那一塊的說(shuō)明。

  • view

手勢(shì)所添加到的視圖

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

手勢(shì)識(shí)別器是否開啟。默認(rèn)YES

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

如果在手勢(shì)識(shí)別器正在識(shí)別手勢(shì)時(shí)將此屬性更改為NO,則手勢(shì)識(shí)別器將轉(zhuǎn)換為已取消狀態(tài)。


取消和延遲觸摸

  • cancelsTouchesInView

識(shí)別成功后--是否向View發(fā)送cancel消息。默認(rèn)YES

@property(nonatomic) BOOL cancelsTouchesInView;

若設(shè)置成NO,表示手勢(shì)識(shí)別成功后不取消響應(yīng)鏈對(duì)事件的響應(yīng),事件依舊會(huì)傳遞給hit-test view。

  • delaysTouchesBegan

手勢(shì)識(shí)別器在識(shí)別手勢(shì)期間,是否截?cái)嗍录?,即不?huì)將事件發(fā)送給hit-tested view。默認(rèn)為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]

可以看出來(lái)多個(gè)手勢(shì)只要有一個(gè)設(shè)置為YES、整條響應(yīng)鏈上的VIew都不會(huì)收到消息。

  • delaysTouchesEnded

當(dāng)手勢(shì)識(shí)別失敗時(shí),若此時(shí)觸摸已經(jīng)結(jié)束,是否延遲調(diào)用響應(yīng)者的 touchesEnded:withEvent。默認(rèn)YES

@property(nonatomic) BOOL delaysTouchesEnded;

若設(shè)置成NO,則在手勢(shì)識(shí)別失敗時(shí)會(huì)立即通知Application發(fā)送狀態(tài)為end的touch事件給hit-tested view以調(diào)用 touchesEnded:withEvent: 結(jié)束事件響應(yīng)。
若設(shè)置成YES,會(huì)延遲大概0.15ms,期間沒有接收到別的touch才會(huì)發(fā)送touchesEnded。


添加依賴

  • requireGestureRecognizerToFail:

只有當(dāng)另一個(gè)手勢(shì)識(shí)別失敗時(shí)才會(huì)除非本手勢(shì)

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

[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
這樣即使singleTapGesture已經(jīng)識(shí)別成功、也會(huì)等到doubleTapGesture識(shí)別失敗再觸發(fā)自身Action


最后

本文主要是自己的學(xué)習(xí)與總結(jié)。如果文內(nèi)存在紕漏、萬(wàn)望留言斧正。如果愿意補(bǔ)充以及不吝賜教小弟會(huì)更加感激。


參考資料

官方文檔-UIGestureRecognizer
你真的了解UIGestureRecognizer嗎?
iOS-UIGestureRecognizer詳解-原理篇
UIGestureRecognizer學(xué)習(xí)筆記
iOS觸摸事件全家桶

最后編輯于
?著作權(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ù)。

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