一.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上,可以使用UIView的addGestureRecognizer:方法將手勢(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è)特性,可參考UIScrollView的delaysContentTouches屬性慰技。這樣屬性也謹(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í)別某些事件
此方法在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;
}
scroll view沒(méi)有滾動(dòng)欄滚局,當(dāng)在scroll view上有觸摸行為時(shí)其要識(shí)別出觸摸行為的目的是scroll view本身還是其內(nèi)容子視圖居暖。定制scrollview如何處理這種情況,看查看UIScrollView類(lèi)的下列屬性和方法藤肢。
touchesShouldBegin:withEvent:inContentView:
touchesShouldCancelInContentView:
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
//定義一個(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];
}