2

ポイントがUIBezierPathにあるかどうかを検出するためにApplesメソッドを使用しようとしています。ただし、「無効なコンテキスト」が返されます。

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

パス内のポイントを決定する方法に関するAppleのドキュメントから直接

- (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は、有効な現在のコンテキストがある場合にのみコンテキストを返します。主な例は次の 2 つです。

  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:]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 に答える