12

In a SO question I asked earlier this year, I got this chunk of code:

MKPolygonView *polygonView = (MKPolygonView *)[self.mapView viewForOverlay:polygon];
MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoord);
CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];

if (CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, FALSE)) {
    // do stuff
}

This works great up until iOS7. It now always returns false and will not detect a point with the path.

I'm trying to find any documentation stating that the method change, but can't find any.

Any ideas why it broke? Or a new solution?

Community
  • 1
  • 1
Padin215
  • 7,444
  • 13
  • 65
  • 103

4 Answers4

22

For some reason (possibly a bug), the path property returns NULL in the current release of iOS 7.

A workaround is to construct your own CGPathRef from the points of the polygon.
With this method, you don't need a reference to the MKPolygonView or the MKPolygonRenderer.

For example:

CGMutablePathRef mpr = CGPathCreateMutable();

MKMapPoint *polygonPoints = myPolygon.points;
//myPolygon is the MKPolygon

for (int p=0; p < myPolygon.pointCount; p++)
{
    MKMapPoint mp = polygonPoints[p];
    if (p == 0)
        CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
    else
        CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}

CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);
//mapPoint above is the MKMapPoint of the coordinate we are testing.
//Putting it in a CGPoint because that's what CGPathContainsPoint wants.

BOOL pointIsInPolygon = CGPathContainsPoint(mpr, NULL, mapPointAsCGP, FALSE);

CGPathRelease(mpr);

This should work on iOS 6 as well.
However, you may want to do this manual construction only if the overlay view's path property returns NULL.

  • Just a note: it looks like if the `MKPolygon` is not visible, the `polygon.path` property will return `NULL` in iOS 6. This solution works like a charm, thank you very much Ms Anna! – Padin215 Sep 26 '13 at 15:22
  • 1
    this worked great. one note: the line "CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);" is critical; my code previously had done this instead "CGPoint polygonViewPoint = [overlayView pointForMapPoint:mapPoint];" which is also broken in iOS7 it seems. – sethpollack Sep 27 '13 at 20:05
  • 1
    @sethpollack: The mapPointAsCGP thing is only to avoid a possible compiler warning that you are passing a MKMapPoint where a CGPoint is expected (_it does not change the values passed -- it just puts them inside a CGPoint struct_). The reason pointForMapPoint would not work here is that the path is built using MKMapPoint units so the point we're testing must also be in MKMapPoint units (not actual screen CGPoint units). –  Dec 05 '13 at 16:30
  • @Anna - I'm trying to test existing map annotations to see if they are inside of the polygon. Any tips on converting the annotation to CGPoint for this evaluation? Previous reference uses the [polygon pointForMapPoint:point]. thanks in advance! – CocoaEv Feb 28 '14 at 20:14
  • @CocoaEv: As mentioned in the code example: `//mapPoint above is the MKMapPoint of the coordinate we are testing.` So you take the annotation's `coordinate` and convert it to an `MKMapPoint` using the `MKMapPointForCoordinate` function. All the "CG" stuff is only because we are using Core Graphics functions (not actual CGPoints). –  Feb 28 '14 at 20:20
  • @Anna- Thank you. wearing dunce cap here. How do I cast MKMapPoint to CGPoint to make CGPathContainsPoint test happy - compiler complains? – CocoaEv Feb 28 '14 at 20:30
  • @CocoaEv: That's what the code example is avoiding by then creating `mapPointAsCGP`. If still an issue, please post a question with the code you're trying. –  Feb 28 '14 at 20:36
  • @Anna - ty,ty,ty. Got it! re-read your solution. MKMapPoint annoPoint = MKMapPointForCoordinate(anOrder.coordinate); CGPoint mapPointAsCGP = CGPointMake(annoPoin.x, annoPoint.y); – CocoaEv Feb 28 '14 at 20:54
2

The issue with your code is that the method

- (MKOverlayView *)viewForOverlay:(id < MKOverlay >)overlay

has been deprecated in iOS 7 (See the doc), you should use this instead:

- (MKOverlayRenderer *)rendererForOverlay:(id < MKOverlay >)overlay

So, in order to make your code work properly in iOS 7 you have to replace:

MKPolygonView *polygonView = (MKPolygonView *)[self.mapView viewForOverlay:polygon];

with

MKPolygonRenderer *polygonView = (MKPolygonRenderer *)[self.mapView rendererForOverlay:polygon];

The problem with this is that - rendererForOverlay: is new in iOS 7, so this change make that your application do not run in previous versions. You can implement two version of the method and make call one or the other depending on the iOS version.

I haven't profile the performance of this in comparison with the @Anna solution

tkanzakic
  • 5,499
  • 16
  • 34
  • 41
  • 1
    The issue is that the `path` property of MKPolygonView/MKPolygonRenderer returns null in iOS 7 (even with rendererForOverlay). In your experience, does `path` have a value in the MKPolygonRenderer after calling rendererForOverlay? –  Dec 05 '13 at 16:34
  • yes, in my case using `rendererForOverlay` path return a non-null value, unless the `polygon` sent as parameter hasn't been added to the mapView before – tkanzakic Dec 05 '13 at 18:20
2

I was having the same issue. Solved it by calling invalidatePath on the MKPolygonRenderer before access the Path.

jdehlin
  • 10,909
  • 3
  • 22
  • 33
1

It's working straight on 7.1 but not on 7.0.

jdehlin response fixed the problem for me with 7.0, ie. calling [view invalidatePath] before each call to

CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, FALSE)
cdrick65
  • 11
  • 2