要理解這兩個方法。先了解一下用戶觸摸屏幕后的事件傳遞過程。
當用戶點擊屏幕后,UIApplication 先響應事件,然后傳遞給UIWindow。如果window可以響應。就開始遍歷window的subviews。遍歷的過程中,如果第一個遍歷的view1可以響應,那就遍歷這個view1的subviews(依次這樣不停地查找,直至查找到合適的響應事件view)。如果view1不可以響應,那就開始對view2進行判斷和子視圖的遍歷。依次類推view3,view4…… 如果最后沒有找到合適的響應view,這個消息就會被拋棄。(整個遍歷的過程就是樹的先序遍歷)。過程如下圖:
理解了上面的圖后,我們再來看看這兩個方法。
為了方便,我們將
- (nullableUIView *)hitTest:(CGPoint)point withEvent:(nullableUIEvent *)event;稱為方法A
- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent *)event;稱為方法B
對view進行重寫這兩個方法后,就會發現,點擊屏幕后,首先響應的是方法A;
如果方法A中,我們沒有調用父類的這個方法,那就根據這個方法A的返回view,作為響應事件的view。(當然返回nil,就是這個view不響應)
如果方法A中,我們調用了父類的這個方法,也就是
[super hitTest:point withEvent:event];那這個時候系統就要調用方法B;通過這個方法的返回值,來判斷當前這個view能不能響應消息。
如果方法B返回的是no,那就不用再去遍歷它的子視圖。方法A返回的view就是可以響應事件的view。
如果方法B返回的是YES,那就去遍歷它的子視圖。(就是上圖我們描述的那樣,找到合適的view返回,如果找不到,那就由方法A返回的view去響應這個事件。)
因此總結下來:
//返回一個view來響應事件 (我們如果不想影響系統的事件傳遞鏈,在這個方法內,最好調用父類的這個方法)
- (nullableUIView *)hitTest:(CGPoint)point withEvent:(nullableUIEvent *)event;
//返回的值可以用來判斷是否繼續遍歷子視圖(返回的根據是觸摸的point是否在view的frame范圍內)
- (BOOL)pointInside:(CGPoint)point withEvent:(nullableUIEvent *)event;