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替換著兩個方法,果然獲取到的響應鏈變了,具體的就是上面的圖片所示。