前言
UI给的图有时候很小,或者有个需求需要我们扩大button的点击区域
我们一般的操作是在button 上添加一个view 增加点击事件,但是我们还有其他更方便的方法去扩大button 的点击区域。
思考
怎样来实现这个功能呢?又有多少种方式可以实现呢?下面一一来讲。
- 理解事件传递过程,用这个来实现扩大点击范围
- 使用Runtime机制扩大点击范围
时间传递过程
当用户点击屏幕后:
UIApplication 先响应事件。
然后传递给UIWindow。
如果window可以响应。就开始遍历window的subviews。遍历原则是:从离用户最近的那个子视图去遍历
遍历的过程中,如果第一个遍历的view1可以响应,那就遍历这个view1的subviews(依次这样不停地查找,直至查找到合适的响应事件view)。
如果view1不可以响应,那就开始对view2进行判断和子视图的遍历。依次类推view3,view4……
如果最后没有找到合适的响应view,这个消息就会被抛弃。这个就是iOS中的事件链。
如下图所示:
然而事件的响应链条是事件链条的逆向,根据视图层级的添加顺序从后往前的
扩大UIButton范围关键的两个方法:UIView方法
// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
// default returns YES if point is in bounds
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;
第一种方法
继承与UIButton,重写下面的方法:
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
CGRect bounds = self.bounds;
//扩大原热区直径至26,可以暴露个接口,用来设置需要扩大的半径。
CGFloat widthDelta = 26;
CGFloat heightDelta = 26;
bounds = CGRectInset(bounds, -0.5 * widthDelta, -0.5 * heightDelta);
return CGRectContainsPoint(bounds, point);
}
第二种:重写方法 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
CGRect rectRange = CGRectInset(self.bounds, -30.0, -30.0);
if (CGRectContainsPoint(rectRange, point)) {
return self;
}else{
return nil;
}
return self;
}
其实我们上面所做的变化其实如果仔细看点击区域还是个矩形,如果需要我们将点击区域规定在圆形范围 内,我们可以这样做:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
[super hitTest:point withEvent:event];
CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
//当然这个半径也可以扩大
CGFloat raidus = self.frame.size.height >= self.frame.size.width ?self.frame.size.width/2 :self.frame.size.width/2;
//传入中心点 实时点击点 与半径判断 点击点是否在半径区域内
BOOL pointInRound =[self touchPointInsideCircle:center radius:raidus targetPoint:point];
if (pointInRound) {
return self;
}else
{
return nil;
}
}
//用来判断 圆形点击区域
- (BOOL)touchPointInsideCircle:(CGPoint)center radius:(CGFloat)radius targetPoint:(CGPoint)point {
CGFloat dist = sqrtf((point.x - center.x) * (point.x - center.x) +
(point.y - center.y) * (point.y - center.y));
return (dist <= radius);
}