Hittest 机制原理
hitTest的原理就是,当我们点击的时候,会触发 window的
hittest方法,在该方法中会首先使用point inside方法判断
点击的地方是否在window范围内,如果在的话,就倒序遍历姿子视图,然后将point转换为相对于子视图的point,然后在子视图再执行hittest,里面执行同样的流程。如果不在window范围内的话,就返回一个nil
这里写下实现逻辑
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
if (self.hidden == NO || self.alpha < 0.05 || self.userInteractionEnabled == NO) {
//1.当满足这几个条件时,直接丢弃touch事件,不再向下分发。
return nil;
}else{
if (![self pointInside:point withEvent:event]) {
//2.如果点击point在视图之外,丢弃
return nil;
}else{
//3.分发给子视图
if (self.subviews.count > 0) {
for (UIView *subView in self.subviews) {
UIView *hitTestSubView = [subView hitTest:point withEvent:event];
return hitTestSubView;
}
}else{
return self;
}
}
}
}
实际应用
扩大点击范围
重写 hitTest 方法,通过UIEdgeInsetsInsetRect 方法生成一个新的frame,然后判断point是否在rect里面
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGRect rect = UIEdgeInsetsInsetRect(self.bounds, self.expandEdgeInset);
if (CGRectContainsPoint(rect, point)) {
return self;
}
return [super hitTest:point withEvent:event];
}
子视图超出父视图可点击
重写 hitTest 方法 在里面不执行 point inside方法(系统默认是要执行的,因为要判断点击的点是否在自己范围内)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
CGPoint pointInSubbutton = [self convertPoint:point toView:self.subButton];
if ([self.subButton hitTest:pointInSubbutton withEvent:event]) {
return [self.subButton hitTest:pointInSubbutton withEvent:event];
}
return [super hitTest:point withEvent:event];
}
其实从上面的逻辑来看,不管我们是扩大点击范围,还是子视图超出范围可以点击,
都是去掉了系统hitest方法中的point inside 方法