如何通过Hit Test确定圆形UIView中触摸对应的16个CGPoint点位?
解决方案:试剂瓶轮播组件的点击判断
嘿,针对你把LMGaugeView改造成16个试剂瓶轮播组件的需求,其实不需要依赖额外的Hit Test方法——你当前的hitTest:实现是为了检测子视图的点击,但现在你没有对应每个试剂瓶的子视图,所以得换个思路来处理点击判断:
核心思路
既然没有子视图对应每个试剂瓶,我们可以预先计算出16个均匀分布在圆形边缘的点位,然后在点击事件里,判断用户的点击点距离哪个预计算点位最近,且在有效点击范围内,以此确定点击的是哪个试剂瓶。
步骤1:预计算16个均匀分布的点位
首先在视图的layoutSubviews(或者初始化方法里)计算并保存这16个点的坐标:
// 定义一个属性保存点位数组 @property (nonatomic, strong) NSArray<NSValue *> *reagentPoints; - (void)layoutSubviews { [super layoutSubviews]; NSMutableArray *points = [NSMutableArray arrayWithCapacity:16]; CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); // 半径:取视图最小边长的一半,减去试剂瓶的偏移量(根据你的UI调整) CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height)/2 - 10; // 每个点位的角度间隔:2π/16 CGFloat angleStep = 2 * M_PI / 16; for (NSInteger i = 0; i < 16; i++) { // 减去M_PI/2是让第一个点从顶部开始,可根据你的UI需求调整角度起始值 CGFloat angle = angleStep * i - M_PI/2; CGFloat x = center.x + radius * cos(angle); CGFloat y = center.y + radius * sin(angle); [points addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]]; } self.reagentPoints = points; }
步骤2:处理点击事件,匹配对应试剂瓶
你可以用系统的touchesBegan:withEvent:方法,或者添加UITapGestureRecognizer来处理点击,这里以touchesBegan为例:
// 可以定义一个block回调给外部处理点击事件 @property (nonatomic, copy) void(^didTapReagentBottleBlock)(NSInteger index); - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch = touches.anyObject; CGPoint touchPoint = [touch locationInView:self]; CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); // 先过滤掉圆形范围外的无效点击(对应你之前hitTest里的14pt扩展范围) CGFloat maxValidRadius = MIN(self.bounds.size.width, self.bounds.size.height)/2 + 14; CGFloat distanceToCenter = hypot(touchPoint.x - center.x, touchPoint.y - center.y); if (distanceToCenter > maxValidRadius) { return; } // 遍历预计算的点位,找到距离最近且在有效点击阈值内的那个 NSInteger closestIndex = -1; CGFloat minDistance = CGFLOAT_MAX; // 点击阈值:根据试剂瓶的大小调整,比如20pt CGFloat clickThreshold = 20; for (NSInteger i = 0; i < self.reagentPoints.count; i++) { CGPoint point = [self.reagentPoints[i] CGPointValue]; CGFloat distance = hypot(touchPoint.x - point.x, touchPoint.y - point.y); if (distance < minDistance && distance < clickThreshold) { minDistance = distance; closestIndex = i; } } // 如果找到有效点击的试剂瓶,触发回调 if (closestIndex != -1 && self.didTapReagentBottleBlock) { self.didTapReagentBottleBlock(closestIndex); } }
步骤3:优化你的hitTest:方法
原来的hitTest:是用矩形来判断点击范围,我们可以改成圆形范围判断,避免点击视图矩形内但圆形外的区域触发事件:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) { return nil; } CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); CGFloat distanceToCenter = hypot(point.x - center.x, point.y - center.y); // 允许14pt的扩展点击范围,和你之前的逻辑一致 CGFloat validRadius = MIN(self.bounds.size.width, self.bounds.size.height)/2 + 14; if (distanceToCenter <= validRadius) { return self; } return nil; }
为什么不需要额外的Hit Test方法?
Hit Test系列方法的核心是查找响应点击的子视图,但你的场景里没有为每个试剂瓶单独创建子视图,所以用Hit Test没法直接定位到具体的试剂瓶。而通过预计算点位+点击距离匹配的方式,更适合这种无对应子视图的点击判断场景。
内容的提问来源于stack exchange,提问作者Anthony_b
相关产品推荐
相关产品推荐

