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