web-dev-qa-db-fra.com

Instantané de MKMapView dans iOS7

J'essaie de créer un instantané d'une application MKMapView dans iOS7 de la même manière qu'il est recommandé partout pour les versions iOS précédentes:

- (UIImage*) renderMapViewToImage
{
   UIGraphicsBeginImageContextWithOptions(mapView.frame.size, NO, 0.0);
   [mapView.layer renderInContext:UIGraphicsGetCurrentContext()];
   UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext(); 
   return image;
}

Cependant, l'image renvoyée est un rectangle noir avec un point d'emplacement actuel bleu au-dessus. J'ai également essayé d'utiliser différentes sous-couches de mapView, mais le résultat est toujours le même.

Est-ce que quelqu'un sait comment prendre des instantanés MKMapView dans iOS7?

40
Michael

Vous pouvez utiliser MKMapSnapshotter et récupérer le image du MKMapSnapshot résultant. Voir la discussion de la vidéo de la session WWDC 2013, Mise en perspective du kit de cartes .

Par exemple:

MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
options.region = self.mapView.region;
options.scale = [UIScreen mainScreen].scale;
options.size = self.mapView.frame.size;

MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
[snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
    UIImage *image = snapshot.image;
    NSData *data = UIImagePNGRepresentation(image);
    [data writeToFile:[self snapshotFilename] atomically:YES];
}];

Cela dit, la solution renderInContext fonctionne toujours pour moi. Il y a des notes sur le fait de ne le faire que dans la file d'attente principale dans iOS7, mais cela semble toujours fonctionner. Mais MKMapSnapshotter semble être la solution la plus appropriée pour iOS7.


Si vous souhaitez inclure des annotations dans l'instantané, vous devez les dessiner manuellement (!). Ceci est discuté en détail à la fin de la vidéo Putting Map Kit in Perspective . Je dois dire que c'est l'une des implémentations les moins élégantes que j'ai jamais vues Apple conseille. Quoi qu'il en soit, dans iOS, cela pourrait ressembler à:

MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
options.region = self.mapView.region;
options.scale = [UIScreen mainScreen].scale;
options.size = self.mapView.frame.size;

MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
[snapshotter startWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) completionHandler:^(MKMapSnapshot *snapshot, NSError *error) {

    // get the image associated with the snapshot

    UIImage *image = snapshot.image;

    // Get the size of the final image

    CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);

    // Get a standard annotation view pin. Clearly, Apple assumes that we'll only want to draw standard annotation pins!

    MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
    UIImage *pinImage = pin.image;

    // ok, let's start to create our final image

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

    // first, draw the image from the snapshotter

    [image drawAtPoint:CGPointMake(0, 0)];

    // now, let's iterate through the annotations and draw them, too

    for (id<MKAnnotation>annotation in self.mapView.annotations)
    {
        CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
        if (CGRectContainsPoint(finalImageRect, point)) // this is too conservative, but you get the idea
        {
            CGPoint pinCenterOffset = pin.centerOffset;
            point.x -= pin.bounds.size.width / 2.0;
            point.y -= pin.bounds.size.height / 2.0;
            point.x += pinCenterOffset.x;
            point.y += pinCenterOffset.y;

            [pinImage drawAtPoint:point];
        }
    }

    // grab the final image

    UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // and save it

    NSData *data = UIImagePNGRepresentation(finalImage);
    [data writeToFile:[self snapshotFilename] atomically:YES];
}];

Pour la mise en œuvre de MacOS, consultez cette vidéo pour plus d'informations, mais la technique est essentiellement la même (le mécanisme de création des images est légèrement différent).

118
Rob

Pour Swift 3

Voici une version Swift 3 que j'ai modifiée à partir de cet article: Rendre une carte en tant qu'image à l'aide de MapKit

Le code suivant vous permet de prendre une photo d'une région en fonction du point (1 coordonnée) et de la polyligne (plusieurs coordonnées)

func takeSnapShot() {
    let mapSnapshotOptions = MKMapSnapshotOptions()

    // Set the region of the map that is rendered. (by one specified coordinate)
    // let location = CLLocationCoordinate2DMake(24.78423, 121.01836) // Apple HQ
    // let region = MKCoordinateRegionMakeWithDistance(location, 1000, 1000)

    // Set the region of the map that is rendered. (by polyline)
    // var yourCoordinates = [CLLocationCoordinate2D]()  <- initinal this array with your polyline coordinates
    let polyLine = MKPolyline(coordinates: &yourCoordinates, count: yourCoordinates.count)
    let region = MKCoordinateRegionForMapRect(polyLine.boundingMapRect)

    mapSnapshotOptions.region = region

    // Set the scale of the image. We'll just use the scale of the current device, which is 2x scale on Retina screens.
    mapSnapshotOptions.scale = UIScreen.main.scale

    // Set the size of the image output.
    mapSnapshotOptions.size = CGSize(width: IMAGE_VIEW_WIDTH, height: IMAGE_VIEW_HEIGHT)

    // Show buildings and Points of Interest on the snapshot
    mapSnapshotOptions.showsBuildings = true
    mapSnapshotOptions.showsPointsOfInterest = true

    let snapShotter = MKMapSnapshotter(options: mapSnapshotOptions)

    snapShotter.start() { snapshot, error in
        guard let snapshot = snapshot else {
            return
        }
        self.imageView.image = snapshot.image
    }
}
5
user2875289

Pour iOS 10 et supérieur , vous pouvez utiliser la classe UIGraphicsImageRenderer pour rendre n'importe quelle vue en image (juste au cas où vous ne voudriez pas utilisez MKMapSnapshotter, puisque j'utilise MapBox).

let render = UIGraphicsImageRenderer(size: self.mapView.bounds.size)
let image = render.image { ctx in
  self.mapView.drawHierarchy(in: self.mapView.bounds, afterScreenUpdates: true)
}

Résultat :

enter image description here

2
Ratnesh Jain