Gesture Recognizers與觸摸事件分發


http://blog.csdn.net/chun799/article/details/8194893


一.Gesture Recognizers

Gesture Recognizers是在iOS3.2引入的,可以用來識別手勢、簡化定製視圖事件處理的對象。Gesture Recognizers的基類爲UIGestureRecognizer,這一個抽象基類,定義了實現底層手勢識別行爲的編程接口。在UIKit框架中提供了6個具體的手勢識別類,用來識別常見的手勢。這6個手勢識別器類爲:

爲了識別手勢,需要將Gesture Recognizers關聯到其檢測觸摸事件的view上,可以使用UIViewaddGestureRecognizer:方法將手勢識別器綁定到視圖上。Gesture Recognizers在觸摸事件處理流程中,處於觀察者的角色,其不是view層級結構的一部分,所以也不參與responder chain。在將觸摸事件發送給hit-test view之前,系統會先將觸摸事件發送到hit-test view上綁定的或hit-test view父視圖(superview)上綁定的Gesture Recognizers上。其流程大概如下圖所示:

注:圖中view與Gesture Recognizer的關係是,Gesture Recognizer關聯在view或view的superview(可能多級)上。

二.Gesture Recognizers與事件分發路徑的關係

Gesture Recognizers可能會延遲將觸摸事件發送到hit-test view上,默認情況下,當Gesture Recognizers識別到手勢後,會向hit-test view發送cancel消息,來取消之前發給hit-test view的事件。控制這個流程的是UIGestureRecognizer的三個屬性

cancelsTouchesInView爲YES,表示當Gesture Recognizers識別到手勢後,會向hit-test view發送 touchesCancelled:withEvent:消息來取消hit-test view對此觸摸序列的處理,這樣只有Gesture Recognizers能響應此觸摸序列,hit-test view不再響應。如果爲NO,則不發送touchesCancelled:withEvent:消息給hit-test view,這樣會使Gesture Recognizers和hit-test view同時響應觸摸序列。

delaysTouchesBegan爲NO,表示觸摸序列開始時,而手勢識別器還未識別出此手勢時,touch事件會同時發向hit-test view,這樣在手勢識別器還未識別出此手勢,hit-test view同時也可以收到同樣的觸摸事件。如果爲YES,則在手勢識別器在識別手勢的過程中,不會有任何觸摸事件發送給hit-test view,如果手勢識別器最終識別到了手勢,則也不會發送任何消息(包括touchesCancelled:withEvent:)給hit-test view;若干手勢識別最終沒有識別到手勢,則所有的觸摸事件在發給hit-test view處理。關於這個特性,可參考UIScrollViewdelaysContentTouches屬性。這樣屬性也謹慎使用,使用不當會導致UI無響應。

delaysTouchesEnded,在文檔上的解釋是,當手勢識別器在識別手勢時,對於UITouchPhaseEnded階段的touch會延遲發送給hit-test view,在手勢識別成功後,發送給hit-test view cancel消息,手勢識別失敗時,發送原來的end消息。其給出了了這樣的例子識別雙擊操作的UITapGestureRecognizer對象,其numberOfTapsRequired設爲2,在用戶進行雙擊操作時,如果delaysTouchesEnded爲NO,則hit-test view中的調用序列爲
touchesBegan:withEvent:,
touchesEnded:withEvent:,
touchesBegan:withEvent:,
and touchesCancelled:withEvent:
如果delaysTouchesEnded爲YES,則調用序列爲:
touchesBegan:withEvent:,
touchesBegan:withEvent:,
touchesCancelled:withEvent:,
touchesCancelled:withEvent:
但我在實際測試時,並非如此,實際測試的結果是,如果delaysTouchesEnded爲NO,則調用序列爲:
touchesBegan:withEvent:,
touchesEnded:withEvent:,
TapGestureRecognizer 檢測到雙擊

如果delaysTouchesEnded爲YES,則調用序列爲:
touchesBegan:withEvent:,
touchesEnded:withEvent:,
TapGestureRecognizer 檢測到雙擊
touchesCancelled:withEvent:
這個問題還沒搞清楚!

三.多個Gesture Recognizer之間的關係

在一個view上可以綁定多個Gesture Recognizer,在默認情況下,觸摸序列中的觸摸事件會以不確定的次序在各個gesture recognizer中傳遞,直到事件最終發送給hit-test view(如果中間沒被Gesture Recognizer識別出並截獲的話)。多個Gesture Recognizer之間的關係也可以根據需要定製,主要有下面幾種行爲

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

以同時識別單擊操作和雙擊操作爲例,兩個gesture recognizers分別用來識別單擊和雙擊,分別爲singleTapGesture和doubleTapGesture。在默認情況下,當用戶進行單擊操作時,singleTapGesture會識別出一個單擊操作,doubleTapGesture也會識別出一個雙擊動作,但我們的意圖是,這僅僅是一個雙擊操作。在這種情況下我們可以使用UIGestureRecognizer的requireGestureRecognizerToFail:方法來使singleTapGesture在doubleTapGesture識別識別的時候才分析事件,如果doubleTapGesture識別出雙擊事件,則singleTapGesture不會有任何動作。

[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];

需要注意的是,在這種情況下,如果用戶進行單擊操作,需要一段延時(即doubleTapGesture識別失敗),singleTapGesture纔會識別出單擊動作,進行單擊處理,這段時間很多,對實際使用幾乎沒有影響。

2.精確控制gesture recognizer是否響應某個事件或事件序列.

UIGestureRecognizerDelegate協議中有兩個可選方法可以控制gesture recognizer是否需要識別某些事件

  • gestureRecognizerShouldBegin:
    此方法在gesture recognizer視圖轉出UIGestureRecognizerStatePossible狀態時調用,如果返回NO,則轉換到UIGestureRecognizerStateFailed;如果返回YES,則繼續識別觸摸序列.(默認情況下爲YES)
  • gestureRecognizer:shouldReceiveTouch:
    此方法在window對象在有觸摸事件發生時,調用gesture recognizer的touchesBegan:withEvent:方法之前調用,如果返回NO,則gesture recognizer不會看到此觸摸事件。(默認情況下爲YES).

另外,在UIGestureRecognizer類中也有兩個可以重寫的方法來完成與Delegate方法中相同的功能
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer;
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer;

3.允許多個手勢識別器共同識別

默認情況下,兩個gesture recognizers不會同時識別它們的手勢,但是你可以實現UIGestureRecognizerDelegate協議中的
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:方法對其進行控制。這個方法在這兩個gesture recognizers中的任意一個將block另一個的觸摸事件時調用,如果返回YES,則兩個gesture recognizers可同時識別,如果返回NO,則並不保證兩個gesture recognizers必不能同時識別,因爲另外一個gesture recognizer的此方法可能返回YES。也就是說兩個gesture recognizers的delegate方法只要任意一個返回YES,則這兩個就可以同時識別;只有兩個都返回NO的時候,纔是互斥的。默認情況下是返回NO。

有這樣一個例子,如果要偵測在window上的所有觸摸事件,可以將gesture recognizer關聯到window上,默認情況下如果手勢被window識別,則子視圖中的gesture recognizer就失效了,而我們在window上的gesture recognizer的目的只是監控所有事件,但並不處理這些事件,具體事件的處理還需要子視圖中的各個gesture recognizer去處理,這樣我們可以實現window上綁定gesture recognizer的delegate方法,使gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:返回YES即可。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

四.UIScrollView的類似行爲

scroll view沒有滾動欄,當在scroll view上有觸摸行爲時其要識別出觸摸行爲的目的是scroll view本身還是其內容子視圖。定製scrollview如何處理這種情況,看查看UIScrollView類的下列屬性和方法。
– touchesShouldBegin:withEvent:inContentView:
– touchesShouldCancelInContentView:
canCancelContentTouches
delaysContentTouches

參考:
Event Handling Guide for iOS – Gesture Recognizers
UIGestureRecognizer Class Reference
UIGestureRecognizerDelegate Protocol Reference
Detecting all touches in an app
UIScrollView Class Reference
How to recognize swipe gesture in UIScrollView
UIGestureRecognizer blocks subview for handling touch events
UIButton touch is delayed when in UIScrollView
Why is scrolling a UITableView much more responsive than scrolling a UIScrollView?
How to cancel touches exactly like UIScrollView?





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章