CGPathContainsPoint 在 iOS 12 中损坏?是否有解决方法?

CGPathContainsPoint broken in iOS 12? Is a workaround possible?

我会尽量保持简短。

拿下面的测试代码:

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) );

此代码 returns 在 iOS 10:

上正确

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

iOS 12 returns:

上的相同代码

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

如您所见,在 iOS 12 CGPathContainsPoint 上说我的测试点在形状内部,即使形状的边界框说它不是。

在 CGPathContainsPoint 中将填充规则更改为 NO 不会改变任何内容。

有解决办法吗? 我以前试过苹果论坛,但似乎没有人在那里回答。

感谢您的帮助!

在与 iOS 10.3 和 12.1

相同的模拟器上确认了这个问题

我不太明白为什么会这样,唯一的线索是如果我们删除这行问题就会消失

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);

这可能是由填充规则或其他原因引起的(例如路径重叠),无论如何,解决方法是创建单独的路径并在之后合并它们的边界框,如下所示(添加了一些绘图代码以可视化事物)

- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGPoint testPoint = CGPointMake(200,185);
    [[UIColor blueColor] setStroke];
    CGContextStrokeEllipseInRect(context, CGRectMake(testPoint.x - 5, testPoint.y - 5, 10, 10));

    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);

    CGRect box1 = CGPathGetBoundingBox(path);

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

    [[UIColor grayColor] setFill];
    [[UIColor redColor] setStroke];
    CGContextAddPath(context, path);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke);
    CGPathRelease(path);

    path = CGPathCreateMutable();
    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);

    CGRect box2 = CGPathGetBoundingBox(path);

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

    [[UIColor yellowColor] setFill];
    [[UIColor greenColor] setStroke];
    CGContextAddPath(context, path);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke);
    CGPathRelease(path);

    CGRect boundingBox = CGRectUnion(box1, box2);
    [[UIColor magentaColor] setStroke];
    CGContextStrokeRect(context, boundingBox);

    NSLog(@"bounding box contains point=%d", CGRectContainsPoint(boundingBox, testPoint));
}

我看到了与您描述的相同的行为。有趣的是,如果您手动关闭路径,它似乎可以工作。例如,这与您的路径相同,除了添加带有注释的行:

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);

我可能建议您尝试自己跟踪起点,并在您关闭子路径的任何地方重新添加一条线,看看是否可以解决问题。


更莫名其妙,做两遍CGPathMoveToPoint(笑)好像也解决了

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);