響應(yīng)者鏈

If I have seen further(than.Descartes)it is by standing upon the shoulders of Giants.

巨人1? ?巨人2??巨人3




一、事件分類

iOS系統(tǒng)操作設(shè)備的方式主要有三種:觸摸屏幕、晃動(dòng)設(shè)備、通過遙控設(shè)施控制設(shè)備。對(duì)應(yīng)的事件類型有以三種:1、觸屏事件(Touch Event)2、運(yùn)動(dòng)事件(Motion Event)3、遠(yuǎn)端控制事件(Remote-Control Event)


事件分類1


事件分類2
處理事件的方法

以觸屏事件(Touch Event)為例,來說明在Cocoa Touch框架中,事件的處理流程。


二、響應(yīng)者鏈(Responder Chain)

UIResponder?。響應(yīng)者對(duì)象是指有響應(yīng)和處理事件能力的對(duì)象。響應(yīng)者鏈就是由一系列的響應(yīng)者對(duì)象構(gòu)成的一個(gè)層次結(jié)構(gòu)。

UIResponder是所有響應(yīng)對(duì)象的基類,在UIResponder類中定義了處理上述各種事件的接口。我們熟悉的UIApplication、 UIViewController、UIWindow和所有繼承自UIView的UIKit類都直接或間接的繼承自UIResponder,所以它們的實(shí)例都是可以構(gòu)成響應(yīng)者鏈的響應(yīng)者對(duì)象。


響應(yīng)者鏈1

響應(yīng)者鏈有以下特點(diǎn):

1、響應(yīng)者鏈通常是由視圖(UIView)構(gòu)成的;

2、一個(gè)視圖的下一個(gè)響應(yīng)者是它視圖控制器(UIViewController)(如果有的話),然后再轉(zhuǎn)給它的父視圖(Super View);

3、視圖控制器(如果有的話)的下一個(gè)響應(yīng)者為其管理的視圖的父視圖;

4、單例的窗口(UIWindow)的內(nèi)容視圖將指向窗口本身作為它的下一個(gè)響應(yīng)者

需要指出的是,Cocoa Touch應(yīng)用不像Cocoa應(yīng)用,它只有一個(gè)UIWindow對(duì)象,因此整個(gè)響應(yīng)者鏈要簡單一點(diǎn);

5、單例的應(yīng)用(UIApplication)是一個(gè)響應(yīng)者鏈的終點(diǎn),它的下一個(gè)響應(yīng)者指向nil,以結(jié)束整個(gè)循環(huán)。


響應(yīng)者鏈2

三、事件分發(fā)(Event Delivery)

第一響應(yīng)者(First responder)指的是當(dāng)前接受觸摸的響應(yīng)者對(duì)象(通常是一個(gè)UIView對(duì)象),即表示當(dāng)前該對(duì)象正在與用戶交互,它是響應(yīng)者鏈的開端。整個(gè)響應(yīng)者鏈和事件分發(fā)的使命都是找出第一響應(yīng)者。

UIWindow對(duì)象以消息的形式將事件發(fā)送給第一響應(yīng)者,使其有機(jī)會(huì)首先處理事件。如果第一響應(yīng)者沒有進(jìn)行處理,系統(tǒng)就將事件(通過消息)傳遞給響應(yīng)者鏈中的下一個(gè)響應(yīng)者,看看它是否可以進(jìn)行處理。

iOS系統(tǒng)檢測到手指觸摸(Touch)操作時(shí)會(huì)將其打包成一個(gè)UIEvent對(duì)象,并放入當(dāng)前活動(dòng)Application的事件隊(duì)列,單例的UIApplication會(huì)從事件隊(duì)列中取出觸摸事件并傳遞給單例的UIWindow來處理,UIWindow對(duì)象首先會(huì)使用hitTest:withEvent:方法尋找此次Touch操作初始點(diǎn)所在的視圖(View),即需要將觸摸事件傳遞給其處理的視圖。

UIWindow實(shí)例對(duì)象會(huì)首先在它的內(nèi)容視圖上調(diào)用hitTest:withEvent:,此方法會(huì)在其視圖層級(jí)結(jié)構(gòu)中的每個(gè)視圖上調(diào)用pointInside:withEvent:(該方法用來判斷點(diǎn)擊事件發(fā)生的位置是否處于當(dāng)前視圖范圍內(nèi),以確定用戶是不是點(diǎn)擊了當(dāng)前視圖),如果pointInside:withEvent:返回YES,則繼續(xù)逐級(jí)調(diào)用,直到找到touch操作發(fā)生的位置,這個(gè)視圖也就是要找的hit-test view。


事件分發(fā)

hitTest:withEvent:方法的處理流程如下:

首先調(diào)用當(dāng)前視圖的pointInside:withEvent:方法判斷觸摸點(diǎn)是否在當(dāng)前視圖內(nèi);

若返回NO,則hitTest:withEvent:返回nil;

若返回YES,則向當(dāng)前視圖的所有子視圖(subviews)發(fā)送hitTest:withEvent:消息,所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖,即從subviews數(shù)組的末尾向前遍歷,直到有子視圖返回非空對(duì)象或者全部子視圖遍歷完畢;

若第一次有子視圖返回非空對(duì)象,則hitTest:withEvent:方法返回此對(duì)象,處理結(jié)束;

如所有子視圖都返回非,則hitTest:withEvent:方法返回自身(self)。


四、說明

1、如果最終hit-test沒有找到第一響應(yīng)者,或者第一響應(yīng)者沒有處理該事件,則該事件會(huì)沿著響應(yīng)者鏈向上回溯,如果UIWindow實(shí)例和UIApplication實(shí)例都不能處理該事件,則該事件會(huì)被丟棄。

2、hitTest:withEvent:方法將會(huì)忽略隱藏(hidden=YES)的視圖,禁止用戶操作(userInteractionEnabled=YES)的視圖,以及alpha級(jí)別小于0.01(alpha<0.01)的視圖。如果一個(gè)子視圖的區(qū)域超過父視圖的bound區(qū)域(父視圖的clipsToBounds 屬性為NO,這樣超過父視圖bound區(qū)域的子視圖內(nèi)容也會(huì)顯示),那么正常情況下對(duì)子視圖在父視圖之外區(qū)域的觸摸操作不會(huì)被識(shí)別,因?yàn)楦敢晥D的pointInside:withEvent:方法會(huì)返回NO,這樣就不會(huì)繼續(xù)向下遍歷子視圖了。當(dāng)然,也可以重寫pointInside:withEvent:方法來處理這種情況。

3、還可以重寫hitTest:withEvent:來達(dá)到某些特定的目的。


五、實(shí)際運(yùn)用

?①如何尋找最合適的view

1.首先判斷主窗口(keyWindow)自己是否能接受觸摸事件

2.觸摸點(diǎn)是否在自己身上

3.從后往前遍歷子控件,重復(fù)前面的兩個(gè)步驟(首先查找數(shù)組中最后一個(gè)元素)

4.如果沒有符合條件的子控件,那么就認(rèn)為自己最合適處理


2個(gè)判斷方法

②攔截事件的處理

1.正因?yàn)閔itTest:withEvent:方法可以返回最合適的view,所以可以通過重寫hitTest:withEvent:方法,返回指定的view作為最合適的view。

2.不管點(diǎn)擊哪里,最合適的view都是hitTest:withEvent:方法中返回的那個(gè)view。

3.通過重寫hitTest:withEvent:,就可以攔截事件的傳遞過程,想讓誰處理事件誰就處理事件。

事件傳遞給誰,就會(huì)調(diào)用誰的hitTest:withEvent:方法。

注 意:如果hitTest:withEvent:方法中返回nil,那么調(diào)用該方法的控件本身和其子控件都不是最合適的view,也就是在自己身上沒有找到更合適的view。那么最合適的view就是該控件的父控件。


六、總結(jié)

①事件的傳遞順序是這樣的:

產(chǎn)生觸摸事件->UIApplication事件隊(duì)列->[UIWindow hitTest:withEvent:]->返回更合適的view->[子控件 hitTest:withEvent:]->返回最合適的view

②事件的傳遞和響應(yīng)的區(qū)別:

事件的傳遞是從上到下(父控件到子控件),事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件。

③特殊情況:

誰都不能處理事件,窗口也不能處理。

重寫window的hitTest:withEvent:方法return nil

只能有窗口處理事件。

控制器的view的hitTest:withEvent:方法return nil或者window的hitTest:withEvent:方法return self

如有問題或疑問歡迎留言交流,一起進(jìn)步。

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容