Search code examples
iosobjective-csdkcore-graphicscgpath

CGPathContainsPoint broken in iOS 12? Is a workaround possible?


I will try to keep this short.

Take the following test code:

CGPath* path =  CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 318.43, 183.47);
CGPathAddCurveToPoint(path, nil, 322.98, 189.32, 327, 194.93, 329.81, 201.62);
CGPathAddLineToPoint(path, nil, 339.36, 182.58);
CGPathCloseSubpath(path);
CGPathMoveToPoint(path, nil, 327.8, 193.04);
CGPathAddCurveToPoint(path, nil, 323.06, 193.04, 323.06, 185.5, 327.8, 185.5);
CGPathAddCurveToPoint(path, nil, 332.54, 185.5, 332.54, 193.04, 327.8, 193.04);
CGPathCloseSubpath(path);
CGRect boundingBox = CGPathGetBoundingBox(path);

CGPoint testPoint = CGPointMake(200,185);

NSLog(@"path contains point=%d | bounding box contains point=%d",CGPathContainsPoint(path, nil,  testPoint, NO), CGRectContainsPoint(boundingBox, testPoint) );

This code returns correctly on iOS 10:

path contains point=0 | bounding box contains point=0

The same code on iOS 12 returns:

path contains point=1 | bounding box contains point=0

As you can see, on iOS 12 CGPathContainsPoint says my testPoint is inside the shape even if the bounding box of the shape says it is not.

Changing the fill rule to NO in CGPathContainsPoint does not change anything.

Is there a solution to this? I tried apple forums before but nobody seems to answer there.

Thank you for any help!


Solution

  • I see the same behavior you describe. Interestingly, if you manually close the path, it seems to work. E.g., this is the same as your path except adding the line with the comment:

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 318.43, 183.47);
    CGPathAddCurveToPoint(path, nil, 322.98, 189.32, 327, 194.93, 329.81, 201.62);
    CGPathAddLineToPoint(path, nil, 339.36, 182.58);
    CGPathAddLineToPoint(path, nil, 318.43, 183.47);  // manually add line back to starting point
    CGPathCloseSubpath(path);
    
    CGPathMoveToPoint(path, nil, 327.8, 193.04);
    CGPathAddCurveToPoint(path, nil, 323.06, 193.04, 323.06, 185.5, 327.8, 185.5);
    CGPathAddCurveToPoint(path, nil, 332.54, 185.5, 332.54, 193.04, 327.8, 193.04);
    CGPathCloseSubpath(path);
    

    I might suggest trying keeping track of the starting point yourself and adding a line back to it wherever you close the subpath and see if that fixes it.


    Even more inexplicably, doing CGPathMoveToPoint twice (lol) also seems to solve it.

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 318.43, 183.47);
    CGPathMoveToPoint(path, nil, 318.43, 183.47);   // move the the starting point twice?!?
    CGPathAddCurveToPoint(path, nil, 322.98, 189.32, 327, 194.93, 329.81, 201.62);
    CGPathAddLineToPoint(path, nil, 339.36, 182.58);
    CGPathCloseSubpath(path);
    
    CGPathMoveToPoint(path, nil, 327.8, 193.04);
    CGPathAddCurveToPoint(path, nil, 323.06, 193.04, 323.06, 185.5, 327.8, 185.5);
    CGPathAddCurveToPoint(path, nil, 332.54, 185.5, 332.54, 193.04, 327.8, 193.04);
    CGPathCloseSubpath(path);