不要已讀不回! iOS 的 Responder Chain

Journey on programming
4 min readDec 30, 2017

--

在 iOS 開發中,充滿了許多與使用者互動的事件,而使用者點擊的動作不會直接透過 APP 來處理,而是必須等到硬體接收到這類的互動之後,再往回尋找到底是哪個 UIView 被點擊了,然後再依照此順序,倒著回來處理點擊的事件。

先講清楚,因為這篇是跟 Responder 有關,所以必須先搞清楚,什麼東西可以來作回應?

繼承自 UIResponder 與其 subClass 都可以來當作回應的對象

而當互動發生的時候,會被生成 touchEvent,然後加到 UIApplication 的處理佇列當中,再把該佇列當中最前面的事件拿出來給 Window 來做處理。事件會透過 Hit-Test Chain 來尋找被點擊到的 UIView,然後再依照此順序回頭來進行 Responder Chain 的尋找誰可以處理此事情,最後才是處理我們所定義好所要處理的內容。

Hit-Test Chain

在 Hit-Test Chain 當中,主要透過下列兩個方法來做判斷

- (UIView)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

第一個方法會回傳在 View Hierarchy 當中同階層的 UIView 實體,第二個方法則會去判斷該次的點擊事件,是否在該 UIView 中出現,如果回傳 true,則會進入下一個層級,然後繼續下去。如果回傳 false,則會再呼叫一次第一個方法,取得另外一個實體來做判斷。如此的動作會一直持續下去,直到找到事件發生的 initial View 為止。

如果今天有個畫面長這樣子,則其 Hit-Test Chain 會如下

UIApplication > Window > View Controller > View Controller.view > A.view > B.view > C.view

Responder Chain

當 Hit-Test Chain 確定之後,將會依照此順序,反著回來尋找哪個 View 可以處理這次的點擊事件。

C.view > B.view > A.view > View Controller.view > View Controller > Window > UIApplication

這時的 Initial View 與 First Responder 都是 C.view ,而這時如果 C.view 無法處理此 touchEvent,就會透過 nextResponder 的方法,往下尋找,如果到了最後一階 UIApplication 都無法處理此事件,則該事件就會被丟棄。

同場加映

大致上 iOS 開發都遵從此定義,除了 UIScrollView 之外,因為 UIScrollView 會需要去處理使用者滑動或是放大縮小的事件,因此 ScrollView 當中的 subView 並不會吃到 touchesBegan 的事件,因為都被 ScrollView 吃掉啦!

如果我在 ScrollView 當中加入了一個 ImageView ,這時候事件只會傳遞到 ScrollView 為止,不會傳遞到 ImageView 當中。

那該如何解決此問題?

  1. 如果使用者不需要 ScrollView 的功能,例如說滑動、放大縮小等等

在該情況下,我們可以直接把 scrollView 與使用者互動的開關關掉,這樣就可以事件再繼續往下傳,傳到其 subView 來做處理。

2. 如果我需要 ScrollView 的功能呢?

必須自己寫一個 UIImageView 的 subClass,然後在其 subClass 當中處理使用者點擊事件所要定義的內容。

至於為什麼…目前我也還不知道,但是我猜應該是 Apple 對於原生的 View 有所規範,所以今天如果是自己去subClass 那些方法,就可以執行到我們定義好的內容,如果有別的想法歡迎讓我知道啊~~

請參考下列文章:

好的,以上說明,希望能幫助你們多了解 iOS 一些,如果有任何問題歡迎讓我知道。 2017 最後的一篇文章,祝大家新年快樂!

--

--

Journey on programming

Software Developer at 91APP. If you like my articles, please clap and follow me on Medium. Never stay still, never plateau!