Creating an MKMapSnapshotter with an MKPolylineRenderer
Asked Answered
F

1

13

I thought iOS 7's MKMapSnapshotters would be a simple way to take a snapshot of an MKMapView, the benefit is that you can do it without loading the map into view. Even though it seems like more work to add pins and overlays (because of the need for core graphics). The WWDC videos give a very good example of creating an MKMapSnapshotter with adding an MKAnnotationView.

However, for someone with not a lot of core graphics experience it's not really obvious how you create an MKMapSnapshotter from an MKPolylineRenderer.

I have tried to do this, but the path is inaccurate. It draws about 10% of the line accurately, but then draws the rest of the path straight.

Here's my code:

MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:snapshotOptions];

[snapshotter startWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
          completionHandler:^(MKMapSnapshot *snapshot, NSError *error)
{
            if (error == nil)
            {
                UIImage *mapSnapshot = [snapshot image];

                UIGraphicsBeginImageContextWithOptions(mapSnapshot.size,
                                                       YES,
                                                       mapSnapshot.scale);

                [mapSnapshot drawAtPoint:CGPointMake(0.0f, 0.0f)];

                CGContextRef context = UIGraphicsGetCurrentContext();

                //Draw the points from the MKPolylineRenderer in core graphics for mapsnapshotter...
                MKPolylineRenderer *overlay = (MKPolylineRenderer *)[self.mapView rendererForOverlay:[_mapView.overlays lastObject]];

                if (overlay.path)
                {
                    CGFloat zoomScale = 3.0;

                    [overlay applyStrokePropertiesToContext:context
                                                atZoomScale:zoomScale];

                    CGContextAddPath(context, overlay.path);
                    CGContextSetLineJoin(context, kCGLineJoinRound);
                    CGContextSetLineCap(context, kCGLineCapRound);
                    CGContextStrokePath(context);
                }

                UIImage *pathImage = UIGraphicsGetImageFromCurrentImageContext();

                [map addMapIcon:pathImage];
                UIGraphicsEndImageContext();
            }
}];

Does anyone have a good workable example on how to do this please?

Footstone answered 27/3, 2014 at 15:39 Comment(0)
W
13

Just had the same problem, this code seems to work:

UIImage * res = nil;
UIImage * image = snapshot.image;

UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);
[image drawAtPoint:CGPointMake(0, 0)];

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetStrokeColorWithColor(context,  [COLOR_FLASHBLUE CGColor]);
CGContextSetLineWidth(context,2.0f);
CGContextBeginPath(context);

CLLocationCoordinate2D coordinates[[polyline pointCount]];
[polyline getCoordinates:coordinates range:NSMakeRange(0, [polyline pointCount])];

for(int i=0;i<[polyline pointCount];i++)
{
    CGPoint point = [snapshot pointForCoordinate:coordinates[i]];

    if(i==0)
    {
        CGContextMoveToPoint(context,point.x, point.y);
    }
    else{
        CGContextAddLineToPoint(context,point.x, point.y);

    }
}

CGContextStrokePath(context);

res = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Wilmoth answered 28/3, 2014 at 15:12 Comment(8)
Interesting that you have to explicitly move and add the line to point rather than use CGContextAddPath. Thanks, I haven't tried it yet but will check it out :)Footstone
I'm not sure that you have to do it this way. I didn't know the zoomlevel, so had to use the pointForCoordiinate method.Wilmoth
This worked for me too. I'd still be curious to know why CGContextAddPath doesn't work instead >_<Footstone
What is the purpose of the initial [image drawAtPoint:CGPointMake(0, 0)]; ? Also I have drawn some pin annotation as images onto the snapshot image before this and when I add the route line the pins disappear. Do I need to do them in separate contexts or something?Vespertine
Oh, I had an extra call to draw(at: CGPoint.zero), when I removed that extra one my pins reappeared. But what is that call for?Vespertine
@Vespertine to draw the snapshotted Image into the context. You were probably over-drawing your pins with your original map?Wilmoth
I had an extra call to draw(at) as mentioned in the comments above. But what does that line do?Vespertine
@Vespertine that line draws the map-snapshot image onto the graphical context. you do it at the start so that the rest of your context-drawing commands appear on top of the image.Bornu

© 2022 - 2024 — McMap. All rights reserved.