本文章將講解有關iOS事件的傳遞機制,如有錯誤或者不同的見解愚战,歡迎留言指出胞枕。
iOS的事件有好幾種:Touch Events(觸摸事件)羹与、Motion Events(運動事件,比如重力感應和搖一搖等)爹橱、Remote Events(遠程事件萨螺,比如用耳機上得按鍵來控制手機),其中最常用的應該就是Touch Events了宅荤,基本存在于每個app的每個地方屑迂,今天我們主要就講講它,至于其他兩個事件有興趣的可以自行查閱資料冯键。
在網頁上當我們講到事件惹盼,我們會講到事件響應鏈,我們會講到事件的響應者和事件的傳遞方式(冒泡)惫确,那么在app上手报,其實也離不開這幾個問題,今天我們也重這幾個方面來介紹iOS的事件機制: 1改化、響應鏈是什么時候怎樣構建的掩蛤? 2、事件第一個響應者是怎么確定的陈肛? 3揍鸟、事件第一個響應者確定后,系統是怎樣傳遞事件的句旱?
響應鏈的構建
無論是哪種事件阳藻,其傳遞和響應都與響應鏈息息相關,那么響應鏈到底是一個什么樣的東西呢谈撒? 在UIKit中有一個類:UIResponder腥泥,我們可以看看頭文件的幾個屬性和方法:
UIResponder是所有可以響應事件的類的基類(從名字應該就可以看出來了),其中包括最常見的UIView和UIViewController甚至是UIApplication啃匿,所以我們的UIView和UIViewController都是作為響應事件的載體蛔外。
那么響應鏈跟這個UIResponder有什么關系呢?事實事件響應鏈的形成和事件的響應和傳遞溯乒,UIResponder都幫我們做了很多事夹厌。我們的app中,所有的視圖都是按照一定的結構組織起來的橙数,即樹狀層次結構呼渣,每個view都有自己的superView衡招,包括controller的topmost view(controller的self.view)昙读。當一個view被add到superView上的時候,他的nextResponder屬性就會被指向它的superView逻住,當controller被初始化的時候,self.view(topmost view)的nextResponder會被指向所在的controller迎献,而controller的nextResponder會被指向self.view的superView瞎访,這樣,整個app就通過nextResponder串成了一條鏈吁恍,也就是我們所說的響應鏈扒秸。所以響應鏈就是一條虛擬的鏈,并沒有一個對象來專門存儲這樣的一條鏈冀瓦,而是通過UIResponder的屬性串連起來的伴奥。如下圖:
Hit-Testing View
文章開頭說到有iOS三種event類型,事件傳遞中UIWindow會根據不同的event翼闽,用不同的方式尋找initial object拾徙,initial object決定于當前的事件類型。比如Touch Event感局,UIWindow會首先試著把事件傳遞給事件發(fā)生的那個view尼啡,就是下文要說的hit-testview。對于Motion和Remote Event询微,UIWindow會把例如震動或者遠程控制的事件傳遞給當前的firstResponder崖瞭,有關firstResponder的相關信息請看這里。下面主要講Touch Event的hit-testview撑毛。
有了事件響應鏈书聚,接下來的事情就是尋找響應事件的具體響應者了,我們稱著為:Hit-Testing View藻雌,尋找這個View的過程我們稱著為Hit-Test寺惫。
那么什么是Hit-Test呢,我們可以把它理解為一個探測器蹦疑,通過這個探測器我們可以找到并判斷手指是否點擊在某個視圖上面,換句話說就是通過Hit-Test可以找到手指點擊到的處于屏幕最前面的那個UIView萨驶。
在解釋Hit-Test是怎么工作之前歉摧,先來看看它是什么時候被調用的。前面說Hit-Test是一個探測器腔呜,那么在代碼里面其實就是一個函數叁温,UIView有如下兩個方法:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
每當手指接觸屏幕,UIApplication接收到手指的事件之后核畴,就會去調用UIWindow的hitTest:withEvent:膝但,看看當前點擊的點是不是在window內,如果是則繼續(xù)依次調用subView的hitTest:withEvent:方法谤草,直到找到最后需要的view跟束。調用結束并且hit-test view確定之后莺奸,這個view和view上面依附的手勢,都會和一個UITouch的對象關聯起來冀宴,這個UITouch會作為事件傳遞的參數之一灭贷,我們可以看到UITouch頭文件里面有一個view和gestureRecognizers的屬性,就是hitTest view和它的手勢略贮。
現在知道Hit-Test是什么時候調用了甚疟,那么接下來看看它是怎么工作的。Hit-Test是采用遞歸的方法從view層級的根節(jié)點開始遍歷逃延,看看下面這張圖: