HitTest的幾個應用

關于響應者鏈條的只是一找一大堆,不再贅述,這里提一嘴目標視圖的查詢方式,以及兄弟視圖是如何處理的。
首先視圖是一個樹狀結構,有人管這個查找目標視圖的過程叫做反向前序深度優先遍歷。很貼切的,反向體現在后添加的視圖優先搜索。所以兄弟視圖覆蓋的情況下,會優先響應后添加的視圖,下面看HitTest的幾個應用。

場景1

當要在主界面上覆蓋一個透明視圖,并且在透明視圖上添加了一個按鈕,要求點擊按鈕時響應,點擊透明視圖不響應,但是手勢要傳遞給下面的視圖。

QQ20191029-184803-HD.gif

如上,控制器上覆蓋了屏幕等大小的透明視圖A,黃色視圖添加在A上,紅色視圖添加在視圖的view上,正常情況下,點擊黃色視圖會有響應,點擊紅色視圖沒有響應。如何實現上面的需求呢。透明視圖A的代碼如下:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event);
        if (view == self){
            return nil;
        }
        return view;
    }

解釋一下:返回的點擊目標視圖如果不是自己,說明點擊的是黃色按鈕,返回view,如果是自己當前視圖不響應,由于A和紅色按鈕時兄弟按鈕,按照反向前序深度優先遍歷原則,會繼續遍歷A。

場景2

擴大按鈕的點擊范圍
這個需求還是很常見的,產品總是說點不到~~怎么辦

-(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
    NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:@encode(UIEdgeInsets)];
    objc_setAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(UIEdgeInsets)hitTestEdgeInsets {
    NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS);
    if(value) {
        UIEdgeInsets edgeInsets; [value getValue:&edgeInsets]; return edgeInsets;
    }else {
        return UIEdgeInsetsZero;
    }
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) ||       !self.enabled || self.hidden) {
        return [super pointInside:point withEvent:event];
    }
    
    CGRect relativeFrame = self.bounds;
    CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);
    
    return CGRectContainsPoint(hitFrame, point);
}

解釋下:hitTest通過pointInside方法判斷點擊是否在當前視圖范圍,所以我們創建一個UIView的分類,添加一個hitTestEdgeInsets屬性,重寫pointInside方法,如果hitTestEdgeInsets有值,將當前視圖范圍按照hitTestEdgeInsets作相應擴展。

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

推薦閱讀更多精彩內容