Gesture Recognizers與觸摸事件分發(fā)

一.Gesture Recognizers

Gesture Recognizers是在iOS3.2引入的搔扁,可以用來(lái)識(shí)別手勢(shì)月褥、簡(jiǎn)化定制視圖事件處理的對(duì)象仇穗。Gesture Recognizers的基類(lèi)為UIGestureRecognizer坚冀,這一個(gè)抽象基類(lèi)脊另,定義了實(shí)現(xiàn)底層手勢(shì)識(shí)別行為的編程接口导狡。在UIKit框架中提供了6個(gè)具體的手勢(shì)識(shí)別類(lèi),用來(lái)識(shí)別常見(jiàn)的手勢(shì)偎痛。這6個(gè)手勢(shì)識(shí)別器類(lèi)為:

UITapGestureRecognizer:用來(lái)識(shí)別點(diǎn)擊手勢(shì),包括單擊旱捧,雙擊,甚至三擊等。

UIPinchGestureRecognizer:用來(lái)識(shí)別手指捏合手勢(shì)枚赡。

UIPanGestureRecognizer:用來(lái)識(shí)別拖動(dòng)手勢(shì)氓癌。

UISwipeGestureRecognizer:用來(lái)識(shí)別Swipe手勢(shì)。

UIRotationGestureRecognizer:用來(lái)識(shí)別旋轉(zhuǎn)手勢(shì)贫橙。

UILongPressGestureRecognizer:用來(lái)識(shí)別長(zhǎng)按手勢(shì)贪婉。

為了識(shí)別手勢(shì),需要將Gesture Recognizers關(guān)聯(lián)到其檢測(cè)觸摸事件的view上,可以使用UIViewaddGestureRecognizer:方法將手勢(shì)識(shí)別器綁定到視圖上卢肃。Gesture Recognizers在觸摸事件處理流程中疲迂,處于觀察者的角色,其不是view層級(jí)結(jié)構(gòu)的一部分莫湘,所以也不參與responder chain尤蒿。在將觸摸事件發(fā)送給hit-test view之前,系統(tǒng)會(huì)先將觸摸事件發(fā)送到hit-test view上綁定的或hit-test view父視圖(superview)上綁定的Gesture Recognizers上逊脯。其流程大概如下圖所示:

注:圖中view與Gesture Recognizer的關(guān)系是,Gesture Recognizer關(guān)聯(lián)在view或view的superview(可能多級(jí))上优质。

二.Gesture Recognizers與事件分發(fā)路徑的關(guān)系

Gesture Recognizers可能會(huì)延遲將觸摸事件發(fā)送到hit-test view上,默認(rèn)情況下,當(dāng)Gesture Recognizers識(shí)別到手勢(shì)后军洼,會(huì)向hit-test view發(fā)送cancel消息,來(lái)取消之前發(fā)給hit-test view的事件⊙菰酰控制這個(gè)流程的是UIGestureRecognizer的三個(gè)屬性

cancelsTouchesInView(默認(rèn)為YES)

delaysTouchesBegan(默認(rèn)為NO)

delaysTouchesEnded(默認(rèn)為YES)

cancelsTouchesInView為YES,表示當(dāng)Gesture Recognizers識(shí)別到手勢(shì)后匕争,會(huì)向hit-test view發(fā)送 touchesCancelled:withEvent:消息來(lái)取消hit-test view對(duì)此觸摸序列的處理,這樣只有Gesture Recognizers能響應(yīng)此觸摸序列,hit-test view不再響應(yīng)爷耀。如果為NO,則不發(fā)送touchesCancelled:withEvent:消息給hit-test view,這樣會(huì)使Gesture Recognizers和hit-test view同時(shí)響應(yīng)觸摸序列甘桑。

delaysTouchesBegan為NO,表示觸摸序列開(kāi)始時(shí)歹叮,而手勢(shì)識(shí)別器還未識(shí)別出此手勢(shì)時(shí)跑杭,touch事件會(huì)同時(shí)發(fā)向hit-test view,這樣在手勢(shì)識(shí)別器還未識(shí)別出此手勢(shì),hit-test view同時(shí)也可以收到同樣的觸摸事件咆耿。如果為YES,則在手勢(shì)識(shí)別器在識(shí)別手勢(shì)的過(guò)程中德谅,不會(huì)有任何觸摸事件發(fā)送給hit-test view,如果手勢(shì)識(shí)別器最終識(shí)別到了手勢(shì),則也不會(huì)發(fā)送任何消息(包括touchesCancelled:withEvent:)給hit-test view;若干手勢(shì)識(shí)別最終沒(méi)有識(shí)別到手勢(shì)萨螺,則所有的觸摸事件在發(fā)給hit-test view處理窄做。關(guān)于這個(gè)特性,可參考UIScrollViewdelaysContentTouches屬性慰技。這樣屬性也謹(jǐn)慎使用椭盏,使用不當(dāng)會(huì)導(dǎo)致UI無(wú)響應(yīng)。

delaysTouchesEnded,在文檔上的解釋是吻商,當(dāng)手勢(shì)識(shí)別器在識(shí)別手勢(shì)時(shí)掏颊,對(duì)于UITouchPhaseEnded階段的touch會(huì)延遲發(fā)送給hit-test view,在手勢(shì)識(shí)別成功后,發(fā)送給hit-test view cancel消息,手勢(shì)識(shí)別失敗時(shí)艾帐,發(fā)送原來(lái)的end消息乌叶。其給出了了這樣的例子識(shí)別雙擊操作的UITapGestureRecognizer對(duì)象盆偿,其numberOfTapsRequired設(shè)為2,在用戶(hù)進(jìn)行雙擊操作時(shí),如果delaysTouchesEnded為NO,則hit-test view中的調(diào)用序列為

touchesBegan:withEvent:,

touchesEnded:withEvent:,

touchesBegan:withEvent:,

and touchesCancelled:withEvent:

如果delaysTouchesEnded為YES,則調(diào)用序列為:

touchesBegan:withEvent:,

touchesBegan:withEvent:,

touchesCancelled:withEvent:,

touchesCancelled:withEvent:

但我在實(shí)際測(cè)試時(shí)枉昏,并非如此,實(shí)際測(cè)試的結(jié)果是,如果delaysTouchesEnded為NO,則調(diào)用序列為:

touchesBegan:withEvent:,

touchesEnded:withEvent:,

TapGestureRecognizer 檢測(cè)到雙擊

如果delaysTouchesEnded為YES,則調(diào)用序列為:

touchesBegan:withEvent:,

touchesEnded:withEvent:,

TapGestureRecognizer 檢測(cè)到雙擊

touchesCancelled:withEvent:

這個(gè)問(wèn)題還沒(méi)搞清楚!

三.多個(gè)Gesture Recognizer之間的關(guān)系

在一個(gè)view上可以綁定多個(gè)Gesture Recognizer,在默認(rèn)情況下陈肛,觸摸序列中的觸摸事件會(huì)以不確定的次序在各個(gè)gesture

recognizer中傳遞,直到事件最終發(fā)送給hit-test view(如果中間沒(méi)被Gesture

Recognizer識(shí)別出并截獲的話(huà))兄裂。多個(gè)Gesture Recognizer之間的關(guān)系也可以根據(jù)需要定制句旱,主要有下面幾種行為

1.使其中一個(gè)gesture recognizer失敗的情況下,另一個(gè)gesture recognizer才能分析事件晰奖。

以同時(shí)識(shí)別單擊操作和雙擊操作為例谈撒,兩個(gè)gesture recognizers分別用來(lái)識(shí)別單擊和雙擊,分別為singleTapGesture和doubleTapGesture匾南。在默認(rèn)情況下,當(dāng)用戶(hù)進(jìn)行單擊操作時(shí)啃匿,singleTapGesture會(huì)識(shí)別出一個(gè)單擊操作,doubleTapGesture也會(huì)識(shí)別出一個(gè)雙擊動(dòng)作蛆楞,但我們的意圖是溯乒,這僅僅是一個(gè)雙擊操作。在這種情況下我們可以使用UIGestureRecognizer的requireGestureRecognizerToFail:方法來(lái)使singleTapGesture在doubleTapGesture識(shí)別識(shí)別的時(shí)候才分析事件豹爹,如果doubleTapGesture識(shí)別出雙擊事件裆悄,則singleTapGesture不會(huì)有任何動(dòng)作。

[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];

需要注意的是臂聋,在這種情況下,如果用戶(hù)進(jìn)行單擊操作光稼,需要一段延時(shí)(即doubleTapGesture識(shí)別失敗),singleTapGesture才會(huì)識(shí)別出單擊動(dòng)作孩等,進(jìn)行單擊處理艾君,這段時(shí)間很多,對(duì)實(shí)際使用幾乎沒(méi)有影響肄方。

2.精確控制gesture recognizer是否響應(yīng)某個(gè)事件或事件序列.

UIGestureRecognizerDelegate協(xié)議中有兩個(gè)可選方法可以控制gesture recognizer是否需要識(shí)別某些事件

gestureRecognizerShouldBegin:

此方法在gesture recognizer視圖轉(zhuǎn)出UIGestureRecognizerStatePossible狀態(tài)時(shí)調(diào)用冰垄,如果返回NO,則轉(zhuǎn)換到UIGestureRecognizerStateFailed;如果返回YES,則繼續(xù)識(shí)別觸摸序列.(默認(rèn)情況下為YES)

gestureRecognizer:shouldReceiveTouch:

此方法在window對(duì)象在有觸摸事件發(fā)生時(shí),調(diào)用gesture recognizer的touchesBegan:withEvent:方法之前調(diào)用扒秸,如果返回NO,則gesture recognizer不會(huì)看到此觸摸事件播演。(默認(rèn)情況下為YES).

另外,在UIGestureRecognizer類(lèi)中也有兩個(gè)可以重寫(xiě)的方法來(lái)完成與Delegate方法中相同的功能

-

(BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer;

-

(BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer;

3.允許多個(gè)手勢(shì)識(shí)別器共同識(shí)別

默認(rèn)情況下伴奥,兩個(gè)gesture recognizers不會(huì)同時(shí)識(shí)別它們的手勢(shì),但是你可以實(shí)現(xiàn)UIGestureRecognizerDelegate協(xié)議中的

gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:方法對(duì)其進(jìn)行控制写烤。這個(gè)方法在這兩個(gè)gesture recognizers中的任意一個(gè)將block另一個(gè)的觸摸事件時(shí)調(diào)用,如果返回YES,則兩個(gè)gesture recognizers可同時(shí)識(shí)別拾徙,如果返回NO洲炊,則并不保證兩個(gè)gesture recognizers必不能同時(shí)識(shí)別,因?yàn)榱硗庖粋€(gè)gesture recognizer的此方法可能返回YES。也就是說(shuō)兩個(gè)gesture recognizers的delegate方法只要任意一個(gè)返回YES暂衡,則這兩個(gè)就可以同時(shí)識(shí)別询微;只有兩個(gè)都返回NO的時(shí)候,才是互斥的狂巢。默認(rèn)情況下是返回NO撑毛。

有這樣一個(gè)例子,如果要偵測(cè)在window上的所有觸摸事件唧领,可以將gesture recognizer關(guān)聯(lián)到window上藻雌,默認(rèn)情況下如果手勢(shì)被window識(shí)別,則子視圖中的gesture recognizer就失效了斩个,而我們?cè)趙indow上的gesture recognizer的目的只是監(jiān)控所有事件胯杭,但并不處理這些事件,具體事件的處理還需要子視圖中的各個(gè)gesture recognizer去處理受啥,這樣我們可以實(shí)現(xiàn)window上綁定gesture recognizer的delegate方法做个,使gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:返回YES即可。

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

return YES;

}

四.UIScrollView的類(lèi)似行為

scroll view沒(méi)有滾動(dòng)欄滚局,當(dāng)在scroll view上有觸摸行為時(shí)其要識(shí)別出觸摸行為的目的是scroll view本身還是其內(nèi)容子視圖居暖。定制scrollview如何處理這種情況,看查看UIScrollView類(lèi)的下列屬性和方法藤肢。

touchesShouldBegin:withEvent:inContentView:

touchesShouldCancelInContentView:

canCancelContentTouches

delaysContentTouches


IOS開(kāi)發(fā)之手勢(shì)——UIGestureRecognizer 共存

在 iPhone 或 iPad 的開(kāi)發(fā)中膝但,除了用touchesBegan / touchesMoved / touchesEnded這組方法來(lái)控制使用者的手指觸控外,也可以用UIGestureRecognizer的衍生類(lèi)別來(lái)進(jìn)行判斷谤草。用UIGestureRecognizer的好處在于有現(xiàn)成的手勢(shì),開(kāi)發(fā)者不用自己計(jì)算手指移動(dòng)軌跡莺奸。UIGestureRecognizer的衍生類(lèi)別有以下幾種:

UITapGestureRecognizer

UIPinchGestureRecognizer

UIRotationGestureRecognizer

UISwipeGestureRecognizer

UIPanGestureRecognizer

UILongPressGestureRecognizer

從命名上不難了解這些類(lèi)別所對(duì)應(yīng)代表的手勢(shì)丑孩,分別是 Tap(點(diǎn)一下)、Pinch(二指往內(nèi)或往外撥動(dòng))灭贷、Rotation(旋轉(zhuǎn))温学、Swipe(滑動(dòng),快速移動(dòng))甚疟、Pan (拖移仗岖,慢速移動(dòng))以及 LongPress(長(zhǎng)按)。這些手勢(shì)別在使用上也很簡(jiǎn)單览妖,只要在使用前定義并添加到對(duì)應(yīng)的視圖上即可轧拄。

//定義一個(gè) recognizer, 并加到需要偵測(cè)該手勢(shì)的 UIView 元件上

- (void)viewDidLoad {

UISwipeGestureRecognizer* recognizer;

//handleSwipeFrom 是偵測(cè)到手勢(shì),所要呼叫的方法

recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:selfaction:@selector(handleSwipeFrom)];

//不同的 Recognizer 有不同的實(shí)體變數(shù)

//例如 SwipeGesture 可以指定方向

//而 TapGesture 則可以指定次數(shù)

recognizer.direction = UISwipeGestureRecognizerDirectionUp

[self.view addGestureRecognizer:recognizer];

[recognizer release];

}

- (void)handleSwipeFrom:(UISwipeGestureRecognizer*)recognizer {

//觸發(fā)手勢(shì)事件后讽膏,在這里作些事情

//底下是刪除手勢(shì)的方法

[self.view removeGestureRecognizer:recognizer];

}

問(wèn)題來(lái)了檩电。有些手勢(shì)其實(shí)是互相關(guān)聯(lián)的,例如 Tap 與 LongPress、Swipe與 Pan俐末,或是 Tap 一次與Tap 兩次料按。當(dāng)一個(gè) UIView 同時(shí)添加兩個(gè)相關(guān)聯(lián)的手勢(shì)時(shí),到底我這一下手指頭按的要算是 Tap 還是 LongPress卓箫?如果照預(yù)設(shè)作法來(lái)看载矿,只要「先滿(mǎn)足條件」的就會(huì)跳出并呼叫對(duì)應(yīng)方法,舉例來(lái)說(shuō)烹卒,如果同時(shí)注冊(cè)了 Pan 和 Swipe闷盔,只要手指頭一移動(dòng)就會(huì)觸發(fā) Pan 然后跳出,因而永遠(yuǎn)都不會(huì)發(fā)生 Swipe甫题;單點(diǎn)與雙點(diǎn)的情形也是一樣馁筐,永遠(yuǎn)都只會(huì)觸發(fā)單點(diǎn),不會(huì)有雙點(diǎn)坠非。

那么這個(gè)問(wèn)題有解嗎敏沉?答案是肯定的,UIGestureRecognizer有個(gè)方法叫做requireGestureRecognizerToFail炎码,他可以指定某一個(gè) recognizer盟迟,即便自己已經(jīng)滿(mǎn)足條件了,也不會(huì)立刻觸發(fā)潦闲,會(huì)等到該指定的 recognizer 確定失敗之后才觸發(fā)攒菠。以同時(shí)支持單點(diǎn)與雙點(diǎn)的手勢(shì)為例,代碼如下:

- (void)viewDidLoad {

//單擊的 Recognizer

UITapGestureRecognizer* singleRecognizer;

singleRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:selfaction:@selector(handleSingleTapFrom)];

singleTapRecognizer.numberOfTapsRequired =1;//單擊

[self.view addGestureRecognizer:singleRecognizer];

//雙擊的 Recognizer

UITapGestureRecognizer*double;

doubleRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:selfaction:@selector(handleDoubleTapFrom)];

doubleTapRecognizer.numberOfTapsRequired =2;//雙擊

[self.view addGestureRecognizer:doubleRecognizer];

//關(guān)鍵在這一行歉闰,如果雙擊確定偵測(cè)失敗才會(huì)觸發(fā)單擊

[singleRecognizer requireGestureRecognizerToFail:doubleRecognizer];

[singleRecognizer release];

[doubleRecognizer release];

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辖众,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子和敬,更是在濱河造成了極大的恐慌凹炸,老刑警劉巖贤徒,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碍讯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡工扎,警方通過(guò)查閱死者的電腦和手機(jī)舱痘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)变骡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人芭逝,你說(shuō)我怎么就攤上這事塌碌。” “怎么了铝耻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵誊爹,是天一觀的道長(zhǎng)蹬刷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)频丘,這世上最難降的妖魔是什么办成? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮搂漠,結(jié)果婚禮上迂卢,老公的妹妹穿的比我還像新娘。我一直安慰自己桐汤,他們只是感情好而克,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著怔毛,像睡著了一般员萍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拣度,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天碎绎,我揣著相機(jī)與錄音,去河邊找鬼抗果。 笑死筋帖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冤馏。 我是一名探鬼主播日麸,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼逮光!你這毒婦竟也來(lái)了代箭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涕刚,失蹤者是張志新(化名)和其女友劉穎梢卸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體副女,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蚣旱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碑幅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡塞绿,死狀恐怖沟涨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情异吻,我是刑警寧澤裹赴,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布喜庞,位于F島的核電站,受9級(jí)特大地震影響棋返,放射性物質(zhì)發(fā)生泄漏延都。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一睛竣、第九天 我趴在偏房一處隱蔽的房頂上張望晰房。 院中可真熱鬧,春花似錦射沟、人聲如沸殊者。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猖吴。三九已至,卻和暖如春挥转,著一層夾襖步出監(jiān)牢的瞬間海蔽,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工扁位, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留准潭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓域仇,卻偏偏與公主長(zhǎng)得像刑然,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子暇务,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容