事件傳遞機(jī)制
-
響應(yīng)者鏈的事件傳遞過程:
- 如果當(dāng)前view是控制器的view,那么控制器就是上一個響應(yīng)者,事件就傳遞給控制器;如果當(dāng)前view不是控制器的view,那么父視圖就是當(dāng)前view的上一個響應(yīng)者,事件就傳遞給它的父視圖。
- 在視圖層次結(jié)構(gòu)的最頂級視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對象進(jìn)行處理。
- 如果window對象也不處理,則其將事件或消息傳遞給UIApplication對象。
- 如果UIApplication也不能處理該事件或消息,則將其丟棄。
-
事件處理的整個流程總結(jié):
- 觸摸屏幕產(chǎn)生觸摸事件后,觸摸事件會被添加到由UIApplication管理的事件隊(duì)列中(即,首先接收到事件的是UIApplication)。
- UIApplication會從事件隊(duì)列中取出最前面的事件,把事件傳遞給應(yīng)用程序的主窗口(keyWindow)。
- 主窗口會在視圖層次結(jié)構(gòu)中找到一個最合適的視圖來處理觸摸事件。(至此,第一步已完成)
- 最合適的view會調(diào)用自己的touches方法處理事件
- touches默認(rèn)做法是把事件順著響應(yīng)者鏈條向上拋。
-
事件的傳遞與響應(yīng):
- 當(dāng)一個事件發(fā)生后,事件會從父控件傳給子控件,也就是說由UIApplication -> UIWindow -> UIView -> initial view,以上就是事件的傳遞,也就是尋找最合適的view的過程。
- 接下來是事件的響應(yīng)。首先看initial view能否處理這個事件,如果不能則會將事件傳遞給其上級視圖(inital view的superView);如果上級視圖仍然無法處理則會繼續(xù)往上傳遞;一直傳遞到視圖控制器view controller,首先判斷視圖控制器的根視圖view是否能處理此事件;如果不能則接著判斷該視圖控制器能否處理此事件,如果還是不能則繼續(xù)向上傳 遞;(對于第二個圖視圖控制器本身還在另一個視圖控制器中,則繼續(xù)交給父視圖控制器的根視圖,如果根視圖不能處理則交給父視圖控制器處理);一直到 window,如果window還是不能處理此事件則繼續(xù)交給application處理,如果最后application還是不能處理此事件則將其丟棄。
- 在事件的響應(yīng)中,如果某個控件實(shí)現(xiàn)了touches...方法,則這個事件將由該控件來接受,如果調(diào)用了[supertouches….];就會將事件順著響應(yīng)者鏈條往上傳遞,傳遞給上一個響應(yīng)者;接著就會調(diào)用上一個響應(yīng)者的touches….方法。
PS:事件的傳遞和響應(yīng)的區(qū)別:
事件的傳遞是從上到下(父控件到子控件),事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件)。
PPS:如何做到一個事件多個對象處理:
因?yàn)橄到y(tǒng)默認(rèn)做法是把事件上拋給父控件,所以可以通過重寫自己的touches方法和父控件的touches方法來達(dá)到一個事件多個對象處理的目的。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.自己先處理事件...
NSLog(@"do somthing...");
// 2.再調(diào)用系統(tǒng)的默認(rèn)做法,再把事件交給上一個響應(yīng)者處理
[super touchesBegan:touches withEvent:event];
}
應(yīng)用如何找到最合適的控件來處理事件?
首先判斷主窗口(keyWindow)自己是否能接受觸摸事件
判斷觸摸點(diǎn)是否在自己身上
子控件數(shù)組中從后往前遍歷子控件,重復(fù)前面的兩個步驟(所謂從后往前遍歷子控件,就是首先查找子控件數(shù)組中最后一個元素,然后執(zhí)行1、2步驟)
view,比如叫做fitView,那么會把這個事件交給這個fitView,再遍歷這個fitView的子控件,直至沒有更合適的view為止。
如果沒有符合條件的子控件,那么就認(rèn)為自己最合適處理這個事件,也就是自己是最合適的view。
尋找最合適控件的底層實(shí)現(xiàn)
注意點(diǎn):hitTest方法內(nèi)部會調(diào)用pointInside方法,詢問觸摸點(diǎn)是否在自己身上,當(dāng)遍歷子控件時,傳入的坐標(biāo)點(diǎn)要進(jìn)行轉(zhuǎn)化,將父視圖上的坐標(biāo)點(diǎn)轉(zhuǎn)換到所要傳遞的子視圖上的坐標(biāo)點(diǎn)
hitTest的底層實(shí)現(xiàn):當(dāng)控件接收到觸摸事件的時候,不管能不能處理事件,都會調(diào)用hitTest方法,此方法的底層實(shí)現(xiàn)是:1:先看自己是否能接受觸摸事件 2:再看觸摸點(diǎn)是否在自己身上 3:從后往前遍歷子控件,拿到子控件后,再次重復(fù)1,2步驟,要把父控件上的坐標(biāo)點(diǎn)轉(zhuǎn)換為子控件坐標(biāo)系下的點(diǎn),再次執(zhí)行hitTest方法。
若是最后還沒有找到合適的view,那么就return self,自己就是合適的view