iOS隨筆——UIResponder與UIGestureRecognizer

觸屏事件(Touch Event)


UIResponder

閱讀前請先了解一下以下2個方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

首先通過一個例子來了解一下UIResponder事件分發(fā)和響應者鏈的過程(用戶點擊視圖View E)。

example.png
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.響應者鏈
UIResponder.jpeg

當發(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

UITouch.png

我們會發(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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 在iOS開發(fā)中經常會涉及到觸摸事件。本想自己總結一下,但是遇到了這篇文章,感覺總結的已經很到位,特此轉載。作者:L...
    WQ_UESTC閱讀 6,251評論 4 26
  • 好奇觸摸事件是如何從屏幕轉移到APP內的?困惑于Cell怎么突然不能點擊了?糾結于如何實現(xiàn)這個奇葩響應需求?亦或是...
    Lotheve閱讀 59,634評論 51 604
  • 用戶以多種方式操縱他們的iOS設備,例如觸摸屏幕或搖動設備。 iOS會解釋用戶何時以及如何操作硬件并將此信息傳遞到...
    坤坤同學閱讀 4,137評論 7 19
  • 在IOS開發(fā)中會遇到各種操作事件,通過程序可以對這些事件做出響應。 首先,當發(fā)生事件響應時,必須知道由誰來響應事件...
    烏七貓閱讀 372評論 0 0
  • 什么是事件? iOS中事件分為3大類 : 觸摸事件, 加速計事件和遠程控制事件.當你的手指在手機屏幕上觸摸時, 產...
    iYeso閱讀 236評論 0 1

友情鏈接更多精彩內容