觸屏事件(Touch Event)
UIResponder
閱讀前請先了解一下以下2個方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
首先通過一個例子來了解一下UIResponder事件分發(fā)和響應者鏈的過程(用戶點擊視圖View E)。

1.事件分發(fā)
(1)iOS系統(tǒng)檢測到手指觸摸(Touch)操作時會將其打包成一個UIEvent對象,并放入當前活動Application的事件隊列,單例的UIApplication會從事件隊列中取出觸摸事件并傳遞給單例的UIWindow來處理
(2)A是UIWindow的根視圖,因此,UIWindwo對象會首相對A進行hit-test,顯然用戶點擊的范圍是在A的范圍內,因此,pointInside:withEvent:返回了YES,這時會繼續(xù)檢查A的子視圖
(3)子視圖B點擊的范圍不再B內,因此B分支的pointInside:withEvent:返回NO,對應的hitTest:withEvent:返回nil;子視圖C點擊的范圍在C內,即C的pointInside:withEvent:返回YES
(4)子視圖D點擊的范圍不再D內,因此D的pointInside:withEvent:返回NO,對應的hitTest:withEvent:返回nil;子視圖E點擊的范圍在E內,即E的pointInside:withEvent:返回YES,由于E沒有子視圖(也可以理解成對E的子視圖進行hit-test時返回了nil),因此,E的hitTest:withEvent:會將E返回,再往回回溯,就是C的hitTest:withEvent:返回E--->>A的hitTest:withEvent:返回E。
至此,本次點擊事件的第一響應者就通過響應者鏈的事件分發(fā)邏輯成功的找到了。
補充說明
hitTest:withEvent:方法將會忽略隱藏(hidden=YES)的視圖,禁止用戶操作(userInteractionEnabled=YES)的視圖,以及alpha級別小于0.01(alpha<0.01)的視圖。如果一個子視圖的區(qū)域超過父視圖的bound區(qū)域(父視圖的clipsToBounds 屬性為NO,這樣超過父視圖bound區(qū)域的子視圖內容也會顯示),那么正常情況下對子視圖在父視圖之外區(qū)域的觸摸操作不會被識別,因為父視圖的pointInside:withEvent:方法會返回NO,這樣就不會繼續(xù)向下遍歷子視圖了。當然,也可以重寫pointInside:withEvent:方法來處理這種情況。
2.響應者鏈

當發(fā)生事件響應時,必須知道由誰來響應事件。在 iOS 中,由響應者鏈來對事件進行響應。
(1)所有事件響應的類都是 UIResponder 的子類,響應者鏈是一個由不同對象組成的層次結構,其中的每個對象將依次獲得響應事件消息的機會。當發(fā)生事件時,事件首先被發(fā)送給第一響應者,第一響應者則是通過響應者鏈的事件分發(fā)邏輯來確定的。
(2)如果第一響應者無法響應事件或者hit-test沒有找到第一響應者,事件會隨著響應鏈向上回溯,回溯順序如圖,如果UIWindow實例和UIApplication實例都不能處理該事件,則該事件會被丟棄
(3)一般來說,第一響應者是個視圖對象或者其子類對象,當其被觸摸后事件被交由它處理,如果它不處理,事件就會被傳遞給它的視圖控制器對象 ViewController(如果存在),然后是它的父視圖(superview)對象(如果存在),以此類推,直到頂層視圖。接下來會沿著頂層視圖(top view)到窗口(UIWindow 對象)再到程序(UIApplication 對象)。如果整個過程都沒有響應這個事件,該事件就被丟棄。一般情況下,在響應者鏈中只要由對象處理事件,事件就停止傳遞。
UIGestureRecognizer
假設如果沒有手勢,如果我們需要監(jiān)聽視圖的手勢則需要通過這四個方法
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event;
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event;
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event;
通過這些方法監(jiān)聽用戶手勢行為有明顯的缺點
(1)需要自定義視圖
(2)默認情況,外界不知道監(jiān)聽的觸摸事件情況
(3)不容易規(guī)范的區(qū)分具體手勢行為
因此蘋果推出了一系列的手勢行為,來簡化開發(fā)難度
現(xiàn)在來討論一下,UIGestureRecognizer和UIResponder的優(yōu)先級
如果在視圖A上添加一個Tap手勢,此時點擊視圖E,則會發(fā)現(xiàn)并沒有響應視圖E的action而是執(zhí)行了視圖A上Tap手勢的action

我們會發(fā)現(xiàn)手勢(2)的優(yōu)先級比所綁定的視圖(3)的優(yōu)先級高
到這里我們不妨再推敲一下,為什么要這樣設計這個機制呢?不能等view判斷自己能否處理之后再往下傳遞么?
答:如果是父view是縮放手勢,如果按照依次傳遞會怎么樣?可以看出在處理的時效性可準確性方面不如這么設計好.
那蘋果的文檔在hit-test說的就是最上面的view處理不了再交給后面的view啊?這不矛盾么?
答:這不矛盾,我們看文檔不能斷章取義,不能太機械,蘋果在hit-test中說的是一種宏觀上的表現(xiàn)形式.
hit-test的目標就是抓住touch對應的響應者鏈的頭,這樣我們就可以分發(fā)了,不然我們如何高效去分發(fā)呢?
參考資料
https://hit-alibaba.github.io/interview/iOS/Cocoa-Touch/Event-Handling.html
http://blog.csdn.net/zhoupengju/article/details/52250135