在开发中,有些子控件超出了部分父控件的区域,导致子控件超出的区域处于不可点状态。
为解决这个问题,可以重写父类的2个方法中选其一
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds
1,产生的原因:
事件产生的时候,事件会顺着父控件——>子控件的方式传递,并且由最适合的UIResponse子控件来处理(UIView也是继承UIResponse),但是这个最适合的UIResponse如何找?就是通过上面的2个方法,一直遍历所有控件。而当点击凸出部分的时候,父控件执行了
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
判断点击区域是否发生在自己身上,显然在默认的情况下判断为NO,所以就不会继续往子控件传该事件了,因此凸出的部分就不会接受到点击该事件。
2,解决方案1
了解了原因之后,我们可以重写父类方法(为什么不重写自己的- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; ?, 因为到父类就不往上传了,你还重写本身的方法干嘛呢~)
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
让父类的可点击区域增加,增加的部分为凸出区域,代码如下:
/**
重写点击区域
@param point 点击点
@param event 点击事件
@return 返回布尔值
*/
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// 默认为真直接返回
BOOL inside = [super pointInside:point withEvent:event];
if (inside) {
return inside;
}
// 遍历所有子控件
for (UIView *subView in self.subviews) {
// 转换point坐标系
CGPoint subViewPoint = [subView convertPoint:point fromView:self];
// 如果点击区域落在子控件上
if ([subView pointInside:subViewPoint withEvent:event]) {
return YES;
}
}
return NO;
}
3,解决方案2(别人写的,其实异曲同工)
后来发现别人也提供了一种思路(我还没发现和我的思路谁劣谁优,大神请指教)
既然是寻找最适合的UIResponse来处理,所以直接返回父类本身或者子控件就好了,重写父类的hitTest:(CGPoint)point withEvent:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// 先使用默认的方法来寻找 hit-TestView
UIView *result = [super hitTest:point withEvent:event];
// 如果 result 不为 nil,说明触摸事件发生在 tabbar 里面,直接返回就可以了
if (result) {
return result;
}
// 到这里说明触摸事件不发生在 tabBar 里面
// 这里遍历那些超出的部分就可以了,不过这么写比较通用。
for (UIView *subview in self.subviews) {
// 把这个坐标从tabbar的坐标系转为subview的坐标系
CGPoint subPoint = [subview convertPoint:point fromView:self];
result = [subview hitTest:subPoint withEvent:event];
// 如果事件发生在subView里就返回
if (result) {
return result;
}
}
return nil;
}
至此,就可解决超出范围的点击事件了。