深究響應者鏈

響應者鏈

概念

響應者:對用戶交互動作事件進行響應的對象。

響應者鏈:成爲處理事件的響應者的先後順序鏈。(當用戶點擊屏幕,能都對用戶交互動作事件進行響應的對象組成的鏈條,繼承自NSResponder,響應者鏈能夠中斷.)

 

詳解:

1、Hit-Test 機制

當用戶觸摸(Touch)屏幕進行交互時,系統首先要找到響應者(Responder)。系統檢測到手指觸摸(Touch)操作時,將Touch 以UIEvent的方式加入UIApplication事件隊列中。UIApplication從事件隊列中取出最新的觸摸事件進行分發傳遞到UIWindow進行處理。UIWindow 會通過hitTest:withEvent:方法尋找觸碰點所在的視圖,這個過程稱之爲hit-test view。

hitTest 的順序如下

1 UIApplication -> UIWindow -> Root View -> ··· -> subview

在頂級視圖(Root View)上調用pointInside:withEvent:方法判斷觸摸點是否在當前視圖內;

如果返回NO,那麼hitTest:withEvent:返回nil;

如果返回YES,那麼它會向當前視圖的所有子視圖發送hitTest:withEvent:消息,所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖,即從subviews數組的末尾向前遍歷,直到有子視圖返回非空對象或者全部子視圖遍歷完畢。

如果有subview的hitTest:withEvent:返回非空對象則A返回此對象,處理結束(注意這個過程,子視圖也是根據pointInside:withEvent:的返回值來確定是返回空還是當前子視圖對象的。並且這個過程中如果子視圖的hidden=YES、userInteractionEnabled=NO或者alpha小於0.1都會並忽略);

如果所有subview遍歷結束仍然沒有返回非空對象,則hitTest:withEvent:返回self;

系統就是這樣通過hit test找到觸碰到的視圖(Initial View)進行響應。

2、響應者鏈 (Responder Chain)

有些時候,Touch後系統通過hit test 機制找到了觸碰到的Initial View,但是Initial view並沒有或者無法正常處理此次Touch。這個時候,系統便會通過響應者鏈尋找下一個響應者,以對此次Touc 進行響應。

響應者鏈順序如下:

1 Initial View -> View Controller(如果存在) -> superview -> · ··  -> rootView -> UIWindow -> UIApplication

示意圖

如果一個View有一個視圖控制器(View Controller),它的下一個響應者是這個視圖控制器,緊接着纔是它的父視圖(Super View),如果一直到Root View都沒有處理這個事件,事件會傳遞到UIWindow(iOS中有一個單例Window),此時Window如果也沒有處理事件,便進入UIApplication,UIApplication是一個響應者鏈的終點,它的下一個響應者指向nil,以結束整個循環。

3、實際開發中常見的相關問題

在實際開發中,經常會遇到視圖沒有響應的情況,特別是新手會經常搞不清楚狀況。

一下是視圖沒有響應的幾個情況:

1.userInteractionEnabled=NO

2.hidden=YES;

3.alpha=0~0.01;

4.沒有實現touchesBegan:withEvent:方法,直接執行touchesMove:withEvent:等方法;

5.目標視圖點擊區域不在父視圖的Frame上 (superView背景色爲clear Color的時候經常會忽略這個問題)。

在某些情景下,我們在點擊子視圖的時候仍然需要調用父視圖的touchesBegan:withEvent:等方法,例如我們在父視圖上添加了一個覆蓋範圍了父視圖大部分面積的TableView或ScrollerView 或其他View,而我們要通過父視圖的touchesBegan:withEvent:方法來收鍵盤。

這個時候我們可以通過UIView的類別,重寫touch相關方法,代碼如下:

 

30 -(void)setEnableNextResponder:(BOOL)enableNextResponder {

    objc_setAssociatedObject(self, &enableNextResponderKey, enableNextResponderKey, OBJC_ASSOCIATION_ASSIGN);

}

-(BOOL)enableNextResponder {

    return objc_getAssociatedObject(self, &enableNextResponderKey);

}

-(void)touchesBegan:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event{

    if (self.enableNextResponder) {

        [[self nextResponder] touchesBegan:touches withEvent:event];

        [super touchesBegan:touches withEvent:event];

    }

}

-(void)touchesEnded:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event {

    if (self.enableNextResponder) {

        [[self nextResponder] touchesEnded:touches withEvent:event];

        [super touchesEnded:touches withEvent:event];

    }

}

-(void)touchesMoved:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event {

    if (self.enableNextResponder) {

        [[self nextResponder] touchesMoved:touches withEvent:event];

        [super touchesMoved:touches withEvent:event];

    }

}

-(void)touchesCancelled:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event {

    if (self.enableNextResponder) {

        [[self nextResponder] touchesCancelled:touches withEvent:event];

        [super touchesCancelled:touches withEvent:event];

    }

}</uitouch *></uitouch *></uitouch *></uitouch *>

 

 

 

上方摘自:http://www.cocoachina.com/ios/20160630/16868.html

 

 

面試題如何答:

介紹響應者鏈:

步驟1、簡單介紹

當用戶點擊屏幕,能都對用戶交互動作事件進行響應的對象組成的鏈條,繼承自NSResponder,響應者鏈能夠中斷。

 

有些時候,touch後系統通過hit test機制找到了觸碰的initial View,但是initial view並沒有或者無法正常處理此次Touch。這個時候,系統就會通過響應者鏈尋找下一個響應者,對此次Touch進行響應。

 

響應者鏈條的順序是:initial View—>view Controller(如果存在)—>superview  —>…..—>rootView—>UIWindow —>UIApplication

步驟2、進行昇華

然後可以介紹一下hit-test機制:當用戶點擊屏幕,系統首先找到UIResponder,然後將touch以UIEvent的方式加入到UIApplication事件隊列中。UIApplication從隊列中取出最新的觸摸事件分發傳遞到UIWindow進行處理,UIWindow會通過hitTest:withEvent:方法尋找觸碰點所在的視圖,這個過程稱之爲hit-test view。

hitTest 的順序如下

1UIApplication -> UIWindow -> Root View -> ··· -> subview

之後頂級視圖會向當前視圖的所有子視圖發送hitTest:withEvent:消息,然後就是響應者鏈的過程。如果有subview的hitTest:withEvent:返回非空對象,處理結束

步驟3、最後補充:

 

介紹自己如何認識響應者鏈或者響應者鏈中遇到的坑:

我入坑是因爲在我的應用首頁,TableVeiw最下面的兩個一直都是無法點擊,根據我最初瞭解的響應者鏈,應該是從上到下響應,應該是可以的,後來具體的瞭解了hit-test機制,目標視圖點擊區域不在父視圖的Frame上,所以點擊沒有效果。

以下下是視圖沒有響應的幾個情況:

 

 

1.userInteractionEnabled=NO

2.hidden=YES;

3.alpha=0~0.01;

4.沒有實現touchesBegan:withEvent:方法,直接執行touchesMove:withEvent:等方法;

5.目標視圖點擊區域不在父視圖的Frame上 (superView背景色爲clear Color的時候經常會忽略這個問題)。

 

 

 

 


 

 

 

 

點擊打開鏈接

發佈了229 篇原創文章 · 獲贊 832 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章