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作相应扩展。

你可能感兴趣的:(HitTest的几个应用)