Pergunta

I am trying to first draw a asymmetric polygon on the UIMapView using the MKPolygon as an overlay to it dynamically. After that, the end user should be able to zoom in/out that particular area only while the rest of the map area is kept with no effects.

Simply from an end user perspective, the person should be able to draw some area using his finger on the map view and then zoom that particular area ONLY while the other areas kept not affected by the zoom operation.

Foi útil?

Solução

As far as I know, there is no built in methods to do this. Furthermore, official documentation is stating:

You use this class as-is to display map information and to manipulate the map contents from your application. You can center the map on a given coordinate, specify the size of the area you want to display, and annotate the map with custom information.

So, to do what you are trying to achieve, you need to use two MKMapView, one over the other.

Project link

I made a simple project to show you the functionality, you can find it here: https://github.com/leonardfactory/mapview-polygon-mask

Screenshot

Here you can see the inner mapView displaying a zoomed map in a random polygon. iPhone screenshot with two MKMapView

Explanation

Start adding two MKMapView, either programmatically or with storyboards. After this, you can mask the topmost MKMapView with a CAShapeLayer, which path has been set using points from the MKPolygon.

MKPolygon however only stores MKMapPoint, so we need to transform these in CGPoint. What can we do?

An useful method to do this is provided by MKMapView itself, convertCoordinate:toPointInView:, that translates CLLocationCoordinate2D points into normal CGPoint, for example we can write a method like this:

/**
 *  Convert between MKMapPoint and CGPoint, to be used as masking path point.
 *
 *  @param point The MKMapPoint to be converted
 *
 *  @return The CGPoint, converted in UIView coords from MKMapPoint provided
 */
- (CGPoint) convertPointToMapView:(MKMapPoint) point
{
    return [self.mainMapView convertCoordinate:MKCoordinateForMapPoint(point) toPointToView:self.mainMapView];
}

Given a polygon so, you need only to create a CAShapeLayer path, using this helper method, and apply it as a mask to the other MKMapView, like this:

// Create a CAShapeLayer to hold masking path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGMutablePathRef mask   = CGPathCreateMutable();

// First point...
CGPoint firstPoint      = [self convertPointToMapView:polygon.points[0]];
CGPathMoveToPoint(mask, NULL, firstPoint.x, firstPoint.y);

// Then with some simple CG functions we can draw all the mask
for(NSUInteger i = 0; i < polygon.pointCount - 1; i++)
{
    CGPoint nextPoint   = [self convertPointToMapView:polygon.points[i+1]];
    CGPathAddLineToPoint(mask, NULL, nextPoint.x, nextPoint.y);
}

// Close path
CGPathCloseSubpath(mask);

maskLayer.path  = mask;
CGPathRelease(mask);

// Mask the second MKMapView
self.backgroundMapView.layer.mask           = maskLayer;

Some things to note:

  • polygon is a MKPolygon. If you need to draw one from taps, you can use the MKMapView method convertPoint:toCoordinateInView:, which does the exact opposite of the method converCoordinate:toPointInView I used before.

  • polygon.points is a C array of MKMapPoint (so its type is MKMapPoint *)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top