hitTest:(CGPoint)point withEvent:(UIEvent *)event

UIView的hitTest:方法和pointInside:方法的實現
2015-03-31      0 個評論    來源:zhangping871的專欄   
收藏    我要投稿
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
對於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的消息處理是,當消息被人處理後默認不再向父層傳遞


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章