在贝塞尔路径内检测轻拍

12

我有一个UIView,作为子视图添加到我的视图控制器中。我在那个视图上绘制了一条贝塞尔曲线路径。下面是我的drawRect实现:

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIBezierPath *bpath = [UIBezierPath bezierPath];

    [bpath moveToPoint:CGPointMake(50, 50)];
    [bpath addLineToPoint:CGPointMake(100, 50)];
    [bpath addLineToPoint:CGPointMake(100, 100)];
    [bpath addLineToPoint:CGPointMake(50, 100)];
    [bpath closePath];

    CGContextAddPath(context, bpath.CGPath);
    CGContextSetStrokeColorWithColor(context,[UIColor blackColor].CGColor);
    CGContextSetLineWidth(context, 2.5);
    CGContextStrokePath(context);
    UIColor *fillColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.5 alpha:0.7];
    [fillColor setFill];
    [bpath fill];
}

我希望检测此贝塞尔路径内的轻拍,但不包括位于UIView内且在路径外部的点。例如,在此情况下,如果我的触摸坐标是(10,10),则不应检测到它。我知道CGContextPathContainsPoint,但当触摸在路径内时它并没有帮助。有没有一种方法可以检测贝塞尔路径内的触摸事件?


也许你需要添加 CGPathCloseSubpath。我已经更新了我的答案,请查看。 - Avt
1
检测在此bezier路径内部而不是UIView内部的轻拍。这个bezier路径是画在UIView内部的,那么怎么可能轻拍它却不在UIView内部呢? - matt
@matt:编辑了问题 - blancos
@blancos - 谢谢,只是想确保我们都在同一个页面上! :) - matt
4个回答

21

有一个函数 CGPathContainsPoint() 在你的情况下可能会很有用。

如果您从父视图中获取手势点,请注意坐标可能与您的测试不正确。您可以使用一个方法来转换从特定视图的坐标系到另一个视图的坐标系,具体而言是:

- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view

9
尝试使用UIBezierPath的方法:contains
func contains(_ point: CGPoint) -> Bool

返回一个布尔值,指示接收器围起来的区域是否包含指定的点。

1
CGPathCloseSubpath()不接受bpath作为参数。我已经尝试了bpath.CGPath。 - blancos

9

在Swift中检测贝塞尔路径内的轻触:

在最新版本的Swift中很简单,按照以下步骤操作即可获取您的UIBezierPath触摸事件。

步骤1:-在添加了UIBeizerPath的视图上初始化轻击事件。

///通过轻击检测捕获图层 let tapRecognizer:UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(YourClass.tapDetected(_:))) viewSlices.addGestureRecognizer(tapRecognizer)

步骤2:-创建“tapDetected”方法

  //MARK:- Hit TAP
public func tapDetected(tapRecognizer:UITapGestureRecognizer){
    let tapLocation:CGPoint = tapRecognizer.locationInView(viewSlices)
    self.hitTest(CGPointMake(tapLocation.x, tapLocation.y))


}

步骤三:将“hitTest”方法设为最终方法

  public func hitTest(tapLocation:CGPoint){
        let path:UIBezierPath = yourPath
        if path.containsPoint(tapLocation){
            //tap detected do what ever you want ..;)
        }else{
             //ooops you taped on other position in view
        }
    }



更新:Swift 4

步骤1:在添加了UIBeizerPath的视图上初始化Tap事件。

///Catch layer by tap detection
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourClass.tapDetected(tapRecognizer:)))
viewSlices.addGestureRecognizer(tapRecognizer)

步骤 2:创建“tapDetected”方法

public func tapDetected(tapRecognizer:UITapGestureRecognizer){
    let tapLocation:CGPoint = tapRecognizer.location(in: viewSlices)
    self.hitTest(tapLocation: CGPoint(x: tapLocation.x, y: tapLocation.y))
}

第三步:将“hitTest”方法定义为final

private func hitTest(tapLocation:CGPoint){
    let path:UIBezierPath = yourPath

    if path.contains(tapLocation){
        //tap detected do what ever you want ..;)
    }else{
        //ooops you taped on other position in view
    }
}

1
@AbhimanyuRathore - 感谢您的解释,Diogo,感谢您的Swift 4更新! 我已经在很大程度上使此功能工作,但我在精度方面遇到了问题..我在这里详细描述了问题:https://stackoverflow.com/questions/55963229/why-is-the-hittest-tap-detection-within-a-bezier-path-producing-inaccurate-res,我想知道您两个是否都可以发表评论...任何有关故障排除的提示都将不胜感激。 - Matvey
@DiogoSouza (给你打个标记,以防万一..提前感谢你) - Matvey
亲爱的@Matvey,我在你的问题https://stackoverflow.com/questions/55963229/why-is-the-hittest-tap-detection-within-a-bezier-path-producing-inaccurate-res/55988774#55988774中尝试给出了我的解释。谢谢。 - Abhimanyu Rathore
@Matvey 需要在我们用于UIViewcontroller中的tapDetection的视图内,将UIGraphicsGetCurrentContext替换为CAShapeLayer。 - Abhimanyu Rathore
@Matvey,StyleKit.drawA5函数需要修改,我们需要用CAShapeLayer替换UIGraphicsGetCurrentContext,以提高UIBezierPath的准确性。 - Abhimanyu Rathore

1

以下是Swift 3.1的解决方案(从这里移植过来,这是苹果推荐的解决方案)

func containsPoint(_ point: CGPoint, path: UIBezierPath, inFillArea: Bool) -> Bool {

        UIGraphicsBeginImageContext(self.size)

        let context: CGContext? = UIGraphicsGetCurrentContext()
        let pathToTest = path.cgPath
        var isHit = false

        var mode: CGPathDrawingMode = CGPathDrawingMode.stroke

        if inFillArea {

            // check if UIBezierPath uses EO fill
            if path.usesEvenOddFillRule {
                mode = CGPathDrawingMode.eoFill
            } else {
                mode = CGPathDrawingMode.fill
            }
        } // else mode == stroke

        context?.saveGState()
        context?.addPath(pathToTest)

        isHit = (context?.pathContains(point, mode: mode))!
        context?.restoreGState()

        return isHit
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接