文章目录
Hit -test 机制
过程
当用户触摸(Touch)屏幕进行交互时,系统首先要找到响应者(Responder)。系统检测到手指触摸(Touch)操作时,将Touch 以UIEvent的方式加入UIApplication事件队列中。UIApplication从事件队列中取出最新的触摸事件进行分发传递到UIWindow进行处理。UIWindow 会通过hitTest:withEvent:方法寻找触碰点所在的视图,这个过程称之为hit-test view。
hitTest 的顺序如下
UIApplication -> UIWindow -> Root View -> ··· -> subview
其伪代码如下
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//系统默认会忽略isUserInteractionEnabled设置为NO、隐藏、或者alpha小于等于0.01的视图
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
CGPoint childP = [self convertPoint:point toView:childView];
UIView *hitTestView = [subview hitTest:childP withEvent:event];
if (hitTestView) {
return hitTestView;
}
}
return self;
}
return nil;
}
由以上流程可知,我们是根据pointInside:withEvent:方法来判断点击点是否在button内的
所以,可以通过重写pointInside:withEvent:的方式扩大 button 的点击范围
## 扩大button的点击范围
为了造一个全局可以使用的轮子,采用分类的方式, 重写分类的pointInside:withEvent:方法,
给分类绑定属性
```objectivec
/**
点击区域扩大的 edgeinsets 如 赋值 UIEdgeInsetsMake(10, 20, 30, 40)
代表向上扩大10,
向左扩大20, 向下扩大30 ,向右扩大40
*/
@property(nonatomic, assign) UIEdgeInsets expandHitEdgeInsets;
/**
向上扩展的点击范围
*/
@property(nonatomic, assign) CGFloat expandTopHitEdge;
/**
向左扩展的点击范围
*/
@property(nonatomic, assign) CGFloat expandLeftHitEdge;
/**
向下扩展的点击范围
*/
@property(nonatomic, assign) CGFloat expandBottomHitEdge;
/**
向右扩展的点击范围
*/
@property(nonatomic, assign) CGFloat expandRightHitEdge;
/**
是否自动扩大点击范围,默认关闭。
如果开启,并且按钮width 或 height 小于44 ,则将相应的小于44的
边长的点击范围设置为44
*/
@property(nonatomic, assign) BOOL isAutoHitExpand;
从写分类的方法,
注意:这里用到了 UIEdgeInsetsInsetRect
该方法可是使用一个CGRect 和一个UIEdgeInsets
生成一个新的CGRect
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
CGFloat top = 0;
CGFloat left = 0;
CGFloat bottom = 0;
CGFloat right = 0;
//设置默认的扩展量优先级最低
if (self.isAutoHitExpand) {
if (self.frame.size.height < 44) {
top = (44 - self.frame.size.height)/2.0;
bottom = (44 - self.frame.size.height)/2.0;
}
if (self.frame.size.width < 44) {
left = (44 - self.frame.size.width)/2.0;
right = (44 - self.frame.size.width)/2.0;
}
}
//设置四个偏移量次之
if (!UIEdgeInsetsEqualToEdgeInsets(self.expandHitEdgeInsets, UIEdgeInsetsZero)) {
top = self.expandHitEdgeInsets.top;
left = self.expandHitEdgeInsets.left;
bottom = self.expandHitEdgeInsets.bottom;
right = self.expandHitEdgeInsets.right;
}
//单独设置的偏移量优先级最高
if (self.expandTopHitEdge) {
top = self.expandTopHitEdge;
}
if (self.expandLeftHitEdge) {
left = self.expandLeftHitEdge;
}
if (self.expandBottomHitEdge) {
bottom = self.expandBottomHitEdge;
}
if (self.expandRightHitEdge) {
right = self.expandRightHitEdge;
}
//添加负号使相应的内边距变为向外扩展
UIEdgeInsets hitExpandEdge = UIEdgeInsetsMake(- top, - left, - bottom, -right);
if(UIEdgeInsetsEqualToEdgeInsets(hitExpandEdge, UIEdgeInsetsZero) || !self.enabled || self.hidden) {
return [super pointInside:point withEvent:event];
}
CGRect relativeFrame = self.bounds;
CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, hitExpandEdge);
return CGRectContainsPoint(hitFrame, point);
}
使用
button.expandHitEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
这样就达到了扩大button点击范围的效果
pod ‘LBButtonExpandHitTest’
#使用方法
button.expandHitEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);