UIBezierPath中的CGContextSaveGState 0x0

时间:2013-03-11 20:56:10

标签: ios objective-c uitouch uibezierpath

我正在尝试使用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文档,了解如何确定路径中的某个点

- (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");
            }

        }
}

2 个答案:

答案 0 :(得分:4)

tl; dr:您应该使用CGPathContainsPoint( ... )代替。


出了什么问题

你的问题是你没有上下文来试图获得它

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

如果存在有效的当前上下文,方法UIGraphicsGetCurrentContext将仅返回上下文。

是两个主要的例子
  1. drawRect:内(上下文是您正在绘制的视图)

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

  3. 解决方案

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

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

    修改

    如果你想测试一个笔划路径你可以使用CGPathCreateCopyByStrokingPath()来首先创建一个你正在抚摸的路径的新填充路径(给定一定的宽度等)。 Ole Begemann有a very good explanation on his blog关于如何做到这一点(包括一些示例代码)。

答案 1 :(得分: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可能有些昂贵,因此您可能需要对路径进行一次描边,并保存描边副本,而不是每次需要测试点时都要对它们进行描边。