IOS視圖繼承關系和事件響應鏈

UIView:UIView->UIResponder->NSObject

UIViewController:UIViewController->UIResponder->NSObject

UIWindow:UIWindow->UIView

UIButton/UISwitch/UITextField:UIButton/UISwitch/UITextField->UIControl->UIView

UILabel:UILabel->UIView

CALayer:CALayer->NSObject


UIWindow算是一種特殊的View,他默認在視圖最頂端,有自己的一套優先級。可以創建多個,控制顯示級別。

CALayer和UIView是相互依賴的,繼承于UIView的都有layer這個屬性,CALayer用于渲染視圖,繪制具體的像素。UIView只是提供一個容器。真正繪制內容的是CALayer。UIView的主layer以外,對它的subLayer,也就是子layer的屬性進行更改,系統將自動進行動畫生成。

從上面的繼承關系可以看出,繼承于UIResponder的才有用戶點擊事件。

UIControl是控件類的基類,它是一個抽象基類,我們不能直接使用UIControl類來實例化控件,它只是為控件子類定義一些通用的接口,并提供一些基礎實現,以在事件發生時,預處理這些消息并將它們發送到指定目標對象上。他是將UIResponder中的復雜觸摸事件封裝成了簡單事件。

NSObject是所有控件的基類。

UIView內部分三個樹:

第一份,邏輯樹,就是代碼里可以操縱的,例如更改layer的屬性等等就在這一份。

第二份,動畫樹,這是一個中間層,系統正在這一層上更改屬性,進行各種渲染操作。

第三份,顯示樹,這棵樹的內容是當前正被顯示在屏幕上的內容。

這三棵樹的邏輯結構都是一樣的,區別只有各自的屬性。


說一下事件響應鏈。

具體的事件響應方式就不介紹了,幾種手勢那種。

用戶點擊屏幕以后我們可以通過hitTest:withEvent:來獲取點擊的點和view。

在iOS中不是任何對象都能處理事件,只有繼承了UIResponder的對象才能接受并處理事件,我們稱之為“響應者對象”。這個上面已經講過了。

假設現在有個頁面,VC->View(兩個子視圖-BView,DView)->BView(子視圖CView)

這時候用戶點擊了CView,響應鏈:

用戶觸摸->產生觸摸事件->UIApplication事件隊列->UIWindow->UIView->AView->DView->BView->CView

也就是說,當用戶產生觸摸事件以后,系統會先去查找application,然后交給window,如果接收不了會繼續遍歷VC中的View,這個時候是優先subView中的最后一個,也就是視圖最頂端,如果找不到繼續遍歷View的子視圖,同樣的方式,優先遍歷subView中最后一個。直到找到響應者。

注意:

這里有這樣一個方法,- (nullable UIResponder *)nextResponder,獲取當前view的下一個響應者,但是有前輩已經發現這個方法查找出來的響應鏈是錯誤的,因為他查找到的是所有response,包括VC。但是很明顯VC并不應該是用戶事件的響應者。如果通過這個方法來查找下一個響應視圖得到的結果是錯誤的。

然后查閱View里面的方法,大概猜到這才應該是獲取到當前響應事件的下一個響應者View。

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

-?(BOOL)pointInside:(CGPoint)point?withEvent:(nullable?UIEvent?*)event;

然后通過runtime替換著兩個方法,果然獲取到的響應鏈變了,具體的就是上面的圖片所示。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容