4

我有一个开放的 CGPath/UIBezierPath,我想检测用户是否触摸它,即一个点是否在距离路径一定距离内。路径是开放的(即线/曲线,而不是形状)。它可以包含直线和曲线元素。如何获得到命中测试路径的距离?

4

2 回答 2

7

似乎 CGPath/UIBezierPath 都没有这样做的功能。编辑:根据@nielsbot 的建议,您可以使用CGPathApply(…). 不过,计算到弯曲部分的距离并不是那么简单。

但是,我找到了一种巧妙的方法来实现我最初的目标,即命中测试路径:CGPathCreateCopyByStrokingPath(…).

- (BOOL)isPoint:(CGPoint)p withinDistance:(CGFloat)distance ofPath:(CGPathRef)path
{
    CGPathRef hitPath = CGPathCreateCopyByStrokingPath(path, NULL, distance*2, kCGLineCapRound, kCGLineJoinRound, 0);
    BOOL isWithinDistance = CGPathContainsPoint(hitPath, NULL, p, false);
    CGPathRelease(hitPath);
    return isWithinDistance;
}

为了获得更好的性能,您可以缓存 hitPath。也可以通过使用将原始路径添加到 hitPath 来用于封闭路径CGPathAddPath(…)

于 2013-10-19T06:53:31.877 回答
1

对于 Swift 3.0:

final func isPoint(point: CGPoint, withinDistance distance: CGFloat, ofPath path: CGPath) -> Bool {

    if let hitPath = CGPath( __byStroking: path,
                             transform: nil,
                             lineWidth: distance,
                             lineCap: CGLineCap.round,
                             lineJoin: CGLineJoin.miter,
                             miterLimit: 0) {

        let isWithinDistance = hitPath.contains(point)
        return isWithinDistance
    }
    return false
}

利用:

let inside = self.isPoint(point: location, withinDistance: 4, ofPath: myPath!)
if inside{...

当然,正如 Patrick 所指出的,最好缓存 hitPath,而不是每次都创建它。如果您有 UIBezierPath,只需:

 let inside = self.isPoint(point: location, withinDistance: 4, ofPath: myUIPath.cgPath!)
于 2017-02-28T14:20:56.330 回答