2

我正在尝试使用 Apples 方法来检测一个点是否在 UIBezierPath 上。然而,它返回一个“无效的上下文”。

正如您从 NSlog 中看到的,我正在传递一个 UIBezierPath 和 A 点来检查。就我而言,是一个接触点。

我不明白为什么。有人可以向我解释或指出正确的方向吗?

NSLOG -----

Path <UIBezierPath: 0x7f57110>
Contains point Path <UIBezierPath: 0x7f57110>
Touch point 425.000000 139.000000
<Error>: CGContextSaveGState: invalid context 0x0
<Error>: CGContextAddPath: invalid context 0x0
<Error>: CGContextPathContainsPoint: invalid context 0x0
<Error>: CGContextRestoreGState: invalid context 0x0
NO

直接来自 Apples Documentation 关于如何确定路径中的点

- (BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath *)path inFillArea:(BOOL)inFill {

    NSLog(@"contains point Path %@", path);
    NSLog(@"Touch point %f %f", point.x, point.y );

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGPathRef cgPath = path.CGPath;
    BOOL    isHit = NO;
// Determine the drawing mode to use. Default to detecting hits on the stroked portion of the path.
    CGPathDrawingMode mode = kCGPathStroke;

    if (inFill) { // Look for hits in the fill area of the path instead.
        if (path.usesEvenOddFillRule)
            mode = kCGPathEOFill;
        else
            mode = kCGPathFill;
    }
 // Save the graphics state so that the path can be removed later.
    CGContextSaveGState(context);
    CGContextAddPath(context, cgPath);

    // Do the hit detection.
    isHit = CGContextPathContainsPoint(context, point, mode);

    CGContextRestoreGState(context);

    return isHit;
}

这是我的 touchesBegan 方法。我在 NSMutableArray 中有我的路径。我解析数组以检查我所有的路径,看看是否有任何被触及。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

 CGPoint curPoint = [[touches anyObject] locationInView:self];
  for (int i = 0; i < [pathInfo count]; i++){
            NSArray *row = [[NSArray alloc] initWithArray:[pathInfo objectAtIndex:i]];
            UIBezierPath *path = [row objectAtIndex:0];

            NSLog(@"Path %@", path);
if ([self containsPoint:curPoint onPath:path inFillArea:NO]){
               NSLog(@"YES");
            } else {
                NSLog(@"NO");
            }

        }
}
4

2 回答 2

4

tl; dr:您应该CGPathContainsPoint( ... )改用。


什么地方出了错

您的问题是您没有尝试获取它的上下文

CGContextRef context = UIGraphicsGetCurrentContext(); // <-- This line here...

UIGraphicsGetCurrentContext如果存在有效的当前上下文,该方法将仅返回一个上下文。两个主要的例子是

  1. 内部drawRect:(上下文是您正在绘制的视图)

  2. 在您自己的图像上下文中(当您使用UIGraphicsBeginImageContext()以便可以使用 Core Graphics 绘制图像时(也许您将其传递给代码的其他部分并将其显示在图像视图中或将其保存到磁盘))。

解决方案

我不知道您为什么要做所有额外的上下文工作,保存和恢复状态等。我似乎错过了简单的方法CGPathContainsPoint()

BOOL isHit = CGPathContainsPoint(
                                 path.CGPath,
                                 NULL,
                                 point,
                                 path.usesEvenOddFillRule
                                 );

编辑

如果您想点击测试笔触路径,您可以使用CGPathCreateCopyByStrokingPath()来首先创建您正在抚摸的路径的新填充路径(给定一定的宽度等)。Ole Begemann在他的博客上对如何做到这一点有很好的解释(包括一些示例代码)。

于 2013-03-11T21:04:29.403 回答
4

CGContextPathContainsPoint方法需要一个图形上下文,Apple 的示例代码从UIGraphicsGetCurrentContext. 但是,UIGraphicsGetCurrentContext只能-[UIView drawRect:]在调用设置 UI 图形上下文的函数内部或之后工作,例如UIGraphicsBeginImageContext.

您可以在没有图形上下文的情况下使用CGPathCreateCopyByStrokingPath(在 iOS 5.0 中添加)和CGPathContainsPoint描边副本执行命中测试:

static BOOL strokedPathContainsPoint(CGPathRef unstrokedPath,
    const CGAffineTransform *transform, CGFloat lineWidth,
    CGLineCap lineCap, CGLineJoin lineJoin, CGFloat miterLimit,
    CGPoint point, bool eoFill)
{
    CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(unstrokedPath,
        transform, lineWidth, lineCap, lineJoin, miterLimit);
    BOOL doesContain = CGPathContainsPoint(strokedPath, NULL, point, eoFill);
    CGPathRelease(strokedPath);
    return doesContain;
}

您必须决定要使用的线宽和其他描边参数。例如:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    CGPoint curPoint = [[touches anyObject] locationInView:self];
    for (int i = 0; i < [pathInfo count]; i++){
        NSArray *row = [[NSArray alloc] initWithArray:[pathInfo objectAtIndex:i]];
        UIBezierPath *path = [row objectAtIndex:0];

        NSLog(@"Path %@", path);
        if (strokedPathContainsPoint(path.CGPath, NULL, 10.0f, kCGLineCapRound,
            kCGLineJoinRound, 0, curPoint, path.usesEvenOddFillRule))
        {
            NSLog(@"YES");
        } else {
            NSLog(@"NO");
        }
    }
}

请注意,这CGPathCreateCopyByStrokingPath可能有点昂贵,因此您可能希望对路径进行一次描边,然后保存描边的副本,而不是每次需要测试点时都对它们进行描边。

于 2013-03-11T21:15:54.863 回答