?背景
對于身處中小公司且業務不怎么復雜的程序員來說,很多技術不常用,你可能看過很多遍也都大致了解,但是實際讓你講,不一定講的清楚。你可能說,我以獨當一面,應對自如了,但是技術的知識甚多,我們常用的只是十之一二,甚至更少。這么多知識,怎么更好的理解和學習?如果實操的場景少,推薦通過AI對話,不斷的提問,然后對他的回答進行反復的驗證校準,加深理解。
今天就住要講一下iOS的事件傳遞和響應
事件傳遞和響應
1、概述
事件傳遞過程:從application傳遞到最上層view,如下圖

事件響應處理過程:從最上層view開始向下傳遞,與事件傳遞方向相反(響應鏈)

2、事件傳遞細節?
當用戶點擊頁面的某個位置時,application將觸發事件傳遞
- 從application到window,到ViewController,到view、subview
- 如果某個view可以處理這個事件,則繼續查找其子view,查找子view的時候從最后添加的開始檢查,一旦某個子view可以處理這個事件則停止遍歷,子view重復此過程。如果子view都不可處理事件則返回自己,事件查找結束。
什么條件算是“可以處理這個事件”,需要同時滿足以下條件
-
view沒有隱藏,hidden?= NO
-
允許交互,userInteractionEnabled = YES
-
透明度alpha > 0.01,注意等于0.01的時候就已經無法響應事件了
-
pointInside: withEvent:返回YES,也就是說view覆蓋區域包含點擊位置。也可以重寫函數,指定一定范圍內的點擊都算到自己身上(常用于擴大按鈕的點擊范圍)。
所以傳遞過程的代碼大致如下
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {// 檢查視圖是否滿足基本條件if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {return nil;}// 檢查觸摸點是否在視圖范圍內if (![self pointInside:point withEvent:event]) {return nil;}// 從后往前遍歷子視圖NSInteger count = self.subviews.count;for (NSInteger i = count - 1; i >= 0; i--) {UIView *subview = self.subviews[i];// 將觸摸點轉換到子視圖的坐標系中CGPoint subPoint = [self convertPoint:point toView:subview];// 遞歸調用子視圖的 hitTest:withEvent: 方法UIView *hitView = [subview hitTest:subPoint withEvent:event];if (hitView) {return hitView;}}// 如果沒有子視圖能處理事件,則返回自身return self;
}
事件傳遞結束后,這個事件的響應鏈就定下來了,響應鏈之外的view就沒有機會處理事件了,即使他可以處理也不行。這里解釋一下,根據前面提到的傳遞規則,對于同一個view的子view,最后添加的擁有絕對的優先權,如果他能處理這個事件,則即使他不處理這個事件,他的兄弟view也么有機會處理。
3、事件響應
事件傳遞結束,響應鏈被確認,則進行事件響應階段
從最后的view開始確認是否處理了事件,如果處理了,則停止向下傳遞,過程結束

首先,根據處理方式的不同,可分為3種:
- UIControl,如按鈕通過 target-action 機制直接將事件傳遞給控件
- 手勢識別器,給view添加gestureRecognizer系列識別,綁定事件回調
- 觸摸事件鏈,通過view的
touchesBegan、touchesEnded來攔截處理的自帶事件
每種處理方式有各自的傳遞鏈條,不會串行,其中
- UIControl,如果在最上層且“可以處理這個事件”,則父視圖的其他方式(touche\gesture)會被阻斷。應該他的內部實現上,將touche和gesture都阻斷了。
- 手勢識別器(gestureRecognizer),如果子view添加了手勢,則點擊子view的時候,父view還會收到
touchesBegan、touchesEnded的回調,也就是說
touche和gesture會同時觸發。如果父子view同時設置了手勢(比如都是tapGesture),則子view會阻斷手勢事件的傳遞,父view不會收到點擊事件。 - 觸摸事件鏈,如果想中斷,則在
touchesBegan中不調用super即可中斷。如果不想中斷,則實現touchesBegan處理事件的同時,可繼續調用super,響應鏈的其他元素就還有機會收到事件和處理事件。
以上就是我對事件傳遞和響應的理解,其重點就是明白傳遞過程,傳遞的條件,響應鏈
那些容易讓誤解的詞語
1、很多文章提到傳遞方向“從上到下”、“從下到上”,這樣子講,并不知道到底從哪到哪傳遞。本文結合了圖片層級做了說明,希望大家能夠明白。
2、查找響應者的過程,很多文章提到“如果view不能處理事件”則...,這種說法我是不贊同的,如果view不能處理事件,那根本就不會傳給他,更別提進入響應鏈了。這里適合的詞應該是“不處理”,或者更恰當點理解是“不攔截”。我們都知道,通過touchesBegan處理事件的時候,如果你調用了super方法,事件還會繼續傳遞,這時候就可以有多個view同時響應事件