- UIView的hitTest:方法和pointInside:方法的實現
-
12345678910111213141516171819202122232425262728293031323334
對於UIView 的兩個方法的講解:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
網上對這兩個方法的講解很多,但是大部分是純文字的描述,我不再贅述,需要可以自己百度“UIView hitTest”等等。
我現在根據我的理解,把這兩個方法的源碼實現模擬出來。
注意:這裏只是模擬,是爲了讓你更容易理解而已,距離真實的源碼還有很大的差距,
比如裏面的event我根本沒用到。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
UIView *touchView = self;
if
([self pointInside:point withEvent:event]) {
for
(UIView *subView in self.subviews) {
//注意,這裏有座標轉換,將point點轉換到subview中,好好理解下
CGPoint subPoint = CGPointMake(point.x - subView.frame.origin.x,
point.y - subView.frame.origin.y);
UIView *subTouchView = [subView hitTest:subPoint withEvent:event];
if
(subTouchView) {
//找到touch事件對應的view,停止遍歷
touchView = subTouchView;
break
;
}
}
}
else
{
//此點不在該View中,那麼連遍歷也省了,直接返回nil
touchView = nil;
}
return
touchView;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
return
CGRectContainsPoint(self.bounds, point);
}
處理原理如下:
? 當用戶點擊屏幕時,會產生一個觸摸事件,系統會將該事件加入到一個由UIApplication管理的事件隊列中
? UIApplication會從事件隊列中取出最前面的事件進行分發以便處理,通常,先發送事件給應用程序的主窗口(UIWindow)
? 主窗口會調用hitTest:withEvent:方法在視圖(UIView)層次結構中找到一個最合適的UIView來處理觸摸事件
(hitTest:withEvent:其實是UIView的一個方法,UIWindow繼承自UIView,因此主窗口UIWindow也是屬於視圖的一種)
? hitTest:withEvent:方法大致處理流程是這樣的:
首先調用當前視圖的pointInside:withEvent:方法判斷觸摸點是否在當前視圖內:
? 若pointInside:withEvent:方法返回NO,說明觸摸點不在當前視圖內,則當前視圖的hitTest:withEvent:返回nil
? 若pointInside:withEvent:方法返回YES,說明觸摸點在當前視圖內,則遍歷當前視圖的所有子視圖(subviews),調用子視圖的hitTest:withEvent:方法重複前面的步驟,子視圖的遍歷順序是從top到bottom,即從subviews數組的末尾向前遍歷,直到有子視圖的hitTest:withEvent:方法返回非空對象或者全部子視圖遍歷完畢:
? 若第一次有子視圖的hitTest:withEvent:方法返回非空對象,則當前視圖的hitTest:withEvent:方法就返回此對象,處理結束
? 若所有子視圖的hitTest:withEvent:方法都返回nil,則當前視圖的hitTest:withEvent:方法返回當前視圖自身(self)
? 最終,這個觸摸事件交給主窗口的hitTest:withEvent:方法返回的視圖對象去處理。
拿到這個UIView後,就調用該UIView的touches系列方法。
1.2、消息處理過程,在找到的那個視圖裏處理,處理完後根據需要,利用響應鏈nextResponder可將消息往下一個響應者傳遞。
UIAppliactionDelegate <- UIWindow <- UIViewController <- UIView <- UIView
【關鍵】:要理解的有三點:1、iOS判斷哪個界面能接受消息是從View層級結構的父View向子View傳遞,即樹狀結構的根節點向葉子節點遞歸傳遞。2、hitTest和pointInside成對,且hitTest會調用pointInside。3、iOS的消息處理是,當消息被人處理後默認不再向父層傳遞