iOS 響應(yīng)者鏈 整理


無奮斗 不清楚




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

先來說說響應(yīng)者對象(Responder Object),顧名思義,指的是有響應(yīng)和處理事件能力的對象。響應(yīng)者鏈就是由一系列的響應(yīng)者對象構(gòu)成的一個(gè)層次結(jié)構(gòu)。

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


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)者

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

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

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

UIWindow對象以消息的形式將事件發(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對象,并放入當(dāng)前活動(dòng)Application的事件隊(duì)列,單例的UIApplication會(huì)從事件隊(duì)列中取出觸摸事件并傳遞給單例的UIWindow來處理,UIWindow對象首先會(huì)使用hitTest:withEvent:方法尋找此次Touch操作初始點(diǎn)所在的視圖(View),即需要將觸摸事件傳遞給其處理的視圖,這個(gè)過程稱之為hit-test view。

UIWindow實(shí)例對象會(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。

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

1.首先調(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ù)組的末尾向前遍歷,直到有子視圖返回非空對象或者全部子視圖遍歷完畢;

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

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


點(diǎn)擊了View E ? ? ?hit-test view的流程

1、A是UIWindow的根視圖,因此,UIWindwo對象會(huì)首相對A進(jìn)行hit-test;

2、顯然用戶點(diǎn)擊的范圍是在A的范圍內(nèi),因此,pointInside:withEvent:返回了YES,這時(shí)會(huì)繼續(xù)檢查A的子視圖;

3、這時(shí)候會(huì)有兩個(gè)分支,B和C:

點(diǎn)擊的范圍不再B內(nèi),因此B分支的pointInside:withEvent:返回NO,對應(yīng)的hitTest:withEvent:返回nil;

點(diǎn)擊的范圍在C內(nèi),即C的pointInside:withEvent:返回YES;

4、這時(shí)候有D和E兩個(gè)分支:

點(diǎn)擊的范圍不再D內(nèi),因此D的pointInside:withEvent:返回NO,對應(yīng)的hitTest:withEvent:返回nil;

點(diǎn)擊的范圍在E內(nèi),即E的pointInside:withEvent:返回YES,由于E沒有子視圖(也可以理解成對E的子視圖進(jìn)行hit-test時(shí)返回了nil),因此,E的hitTest:withEvent:會(huì)將E返回,再往回回溯,就是C的hitTest:withEvent:返回E--->>A的hitTest:withEvent:返回E。

至此,本次點(diǎn)擊事件的第一響應(yīng)者就通過響應(yīng)者鏈的事件分發(fā)邏輯成功的找到了。

不難看出,這個(gè)處理流程有點(diǎn)類似二分搜索的思想,這樣能以最快的速度,最精確地定位出能響應(yīng)觸摸事件的UIView。

三、說明

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

2、hitTest:withEvent:方法將會(huì)忽略

1.忽略隱藏(hidden=YES)的視圖

2.禁止用戶操作(userInteractionEnabled=YES)的視圖

3.alpha級(jí)別小于0.01(alpha<0.01)的視圖

4.如果一個(gè)子視圖的區(qū)域超過父視圖的bound區(qū)域(父視圖的clipsToBounds 屬性為NO,這樣超過父視圖bound區(qū)域的子視圖內(nèi)容也會(huì)顯示)

那么正常情況下對子視圖在父視圖之外區(qū)域的觸摸操作不會(huì)被識(shí)別,因?yàn)楦敢晥D的pointInside:withEvent:方法會(huì)返回NO,這樣就不會(huì)繼續(xù)向下遍歷子視圖了。當(dāng)然,也可以重寫pointInside:withEvent:方法來處理這種情況。


事件傳遞的完整過程

先將事件對象由上往下傳遞(由父控件傳遞給子控件),找到最合適的控件來處理這個(gè)事件。 調(diào)用最合適控件的touches….方法 如果調(diào)用了[super touches….];就會(huì)將事件順著響應(yīng)者鏈條往上傳遞,傳遞給上一個(gè)響應(yīng)者 接著就會(huì)調(diào)用上一個(gè)響應(yīng)者的touches….方法

如何判斷上一個(gè)響應(yīng)者

如果當(dāng)前這個(gè)view是控制器的view,那么控制器就是上一個(gè)響應(yīng)者 如果當(dāng)前這個(gè)view不是控制器的view,那么父控件就是上一個(gè)響應(yīng)者

響應(yīng)者鏈條的事件傳遞過程

如果view是控制器的view,就傳遞給控制器;如不是,則將其傳遞給它的父視圖 在視圖層次結(jié)構(gòu)的最頂級(jí)視圖,如果也不能處理收到的事件或消息,則其將事件或消息傳遞給window對象進(jìn)行處理 如果window對象也不處理,則其將事件或消息傳遞給UIApplication對象 如果UIApplication也不能處理該事件或消息,則將其丟棄

注意:為什么用隊(duì)列管理事件,而不用棧?

隊(duì)列先進(jìn)先出,能保證先產(chǎn)生的事件先處理。棧先進(jìn)后出。

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

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

  • 一篇搞定事件傳遞、響應(yīng)者鏈條、hitTest和pointInside的使用發(fā)生觸摸事件后,系統(tǒng)會(huì)將該事件加入到一個(gè)...
    克魯?shù)吕?/span>閱讀 1,193評論 0 1
  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的?困惑于Cell怎么突然不能點(diǎn)擊了?糾結(jié)于如何實(shí)現(xiàn)這個(gè)奇葩響應(yīng)需求?亦或是...
    Lotheve閱讀 59,425評論 51 604
  • 在iOS開發(fā)中經(jīng)常會(huì)涉及到觸摸事件。本想自己總結(jié)一下,但是遇到了這篇文章,感覺總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載。作者:L...
    WQ_UESTC閱讀 6,236評論 4 26
  • 本文來自:http://ios.jobbole.com/84081/ 前言: 按照時(shí)間順序,事件的生命周期是這樣的...
    HackerOnce閱讀 2,940評論 1 10
  • 今天到鄉(xiāng)下去做冬至,這是老家的一種習(xí)俗,像清明一樣到祖宗的墳前燒燒紙,磕磕頭。加上很久沒有看望父母以及一大家子人也...
    胡義華閱讀 8,005評論 0 2

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