7

Possible Duplicate:
How can I check if a user tapped near a CGPath?

I'm following Apple's guide from here

http://developer.apple.com/library/ios/#documentation/2DDrawing/Conceptual/DrawingPrintingiOS/BezierPaths/BezierPaths.html

to try and detect a touch event only on the stroked portion of my UIBezierPath. If I use the UIBezierPath method, containsPoint, it works fine, but it detects touch events on the stroke and the fill portion of the UIBezierPath, and I want it to only occur on the stroked portion.

Following Apple's guide (at listing 3-6 of the link), I created this function:

- (BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath*)path inFillArea:(BOOL)inFill
{
    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;
}

When I call it, I get:

Error: CGContextSaveGState: invalid context 0x0
Error: CGContextAddPath: invalid context 0x0
Error: CGContextPathContainsPoint: invalid context 0x0
Error: CGContextRestoreGState: invalid context 0x0

Here's my view's drawRect function, and my code to create my path:

- (UIBezierPath *) createPath {
    static UIBezierPath *path = nil;
    if(!path) {
        path = [[UIBezierPath bezierPathWithOvalInRect:CGRectMake(35, 45, 250, 250)] retain];
        path.lineWidth = 50.0;
        [path closePath];
    }    
    return path;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
*/ 
- (void)drawRect:(CGRect)rect
{
    // Drawing code

    //draw a circle
    UIBezierPath *aPath = [self createPath];

    //set stroke width
    aPath.lineWidth = 50.0;

    //set stroke color
    [[UIColor blueColor] setStroke];

    //stroke path
    [aPath stroke];

    //close path
    [aPath closePath];

    //add a label at the "top"
    UILabel *topLabel;
    topLabel = [[UILabel alloc]initWithFrame:CGRectMake(120, -5, 80, 25)];
    topLabel.text = @"The Top";
    [self addSubview:topLabel];
}

I'm then trying to call the containsPoint function inside of touchesMoved, like this:

if (![self containsPoint:c onPath:[self createPath] inFillArea:NO]) return;
Community
  • 1
  • 1
james
  • 83
  • 1
  • 4
  • 1
    Did you ever find a solution for this? This post is #1 on the internet for all questions related to this, but there's no answer :) – shawnwall Jan 23 '12 at 18:46
  • 1
    I add my voice to shawnall's, have you managed to get this solved?? Hope you can share teh solution if you have any – Abolfoooud Aug 28 '12 at 09:56

2 Answers2

4

When you're currently not drawing (as in the touchesMoved method), there is no current context, so UIGraphicsGetCurrentContext() will return NULL.

As all you're doing with the context is to use its current path, you could just as well use CGPathContainsPoint instead of CGContextPathContainsPoint.

omz
  • 53,243
  • 5
  • 129
  • 141
  • That makes sense. Thanks. How would I call CGPathContainsPoint? Calling it like: if (!CGPathContainsPoint([self createPath].CGPath, nil, c, false)) return; Results in the same thing as UIBezierPath.containsPoint, as it seems to pick up both the stroke and fill areas. Calling it like: if (!CGPathContainsPoint([self createPath], nil, c, false)) return; doesn't seem to work at all. – james May 27 '11 at 01:18
  • Ah, you're right, it won't work this way. I didn't notice the fillMode parameter. Apparently you need a CGContext to do hit detection on the stroke part. Perhaps you could create a small bitmap context for this, using CGBitmapContextCreate (you'll find lots of questions about this on SO). – omz May 27 '11 at 01:57
1

Apple's example assumes you're working inside a graphics context. You're not, so you can just add two lines to the method:

UIGraphicsBeginImageContext(self.frame.size); // ADD THIS...
CGContextRef context = UIGraphicsGetCurrentContext(); // ...before this

and

CGContextRestoreGState(context); // after this...
UIGraphicsEndImageContext(); // ADD THIS

I just ran into the same problem, and this fixed it for me!

arlomedia
  • 8,534
  • 5
  • 60
  • 108
  • What does this do exactly? It keeps the context from being nil, but none of my CGPoints are registering within the UIBezierPaths I give it. – johnny Jul 18 '19 at 20:14