swift3mkpointannotationmkmapsnapshotter

Swift 3 Add custom annotation pin to MKMapSnapShotter snapshot


I'm learning Swift 3 on my own, and my current learning project involves allowing the user to snap a photo and get a map snapshot with the current location pinned.

I've relied on this answer from Aug 2015 and this answer from Jun 2016 for guidance, but I still can't find the right path.

Right now, I can...

  1. Get the photo from the buffer
  2. Get a map snapshot

But I just can't place the pin. I know that my code is incomplete and ineffective -- so this is more than just a debugging question. Here is what I've been working with (as well as many variations based on the links above):

let snapShotter = MKMapSnapshotter(options: mapSnapshotOptions)

snapShotter.start() {

    snapshot, error in

        guard let snapshot = snapshot else {
            return
        }

        let image = snapshot.image
        let annotation = MKPointAnnotation()
        annotation.coordinate = needleLocation  // is a CLLocationCoordinate2D
        annotation.title = "My Title"

        let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "annotation")

        // I want to push the final image to a global variable
        // can't figure out how to define finalImage as the pinned map
        self.myMap = finalImage


        } // close snapShotter.start

I've been stuck for days now, so I certainly would appreciate any insights. Thanks!


Solution

  • To render a MKMapSnapshot with annotation views, you have to manually draw the snapshot's image and the annotation view's image on a new graphic context, and get a new image from that context. In Swift 3:

    let rect = imageView.bounds
    
    let snapshot = MKMapSnapshotter(options: options)
    snapshot.start { snapshot, error in
        guard let snapshot = snapshot, error == nil else {
            print(error ?? "Unknown error")
            return
        }
    
        let image = UIGraphicsImageRenderer(size: options.size).image { _ in
            snapshot.image.draw(at: .zero)
    
            let pinView = MKPinAnnotationView(annotation: nil, reuseIdentifier: nil)
            let pinImage = pinView.image
    
            var point = snapshot.point(for: location.coordinate)
    
            if rect.contains(point) {
                point.x -= pinView.bounds.width / 2
                point.y -= pinView.bounds.height / 2
                point.x += pinView.centerOffset.x
                point.y += pinView.centerOffset.y
                pinImage?.draw(at: point)
            }
        }
    
        // do whatever you want with this image, e.g.
    
        DispatchQueue.main.async {
            imageView.image = image
        }
    }
    

    This was adapted from https://stackoverflow.com/a/18776723/1271826, which itself was adapted from WWDC 2013 video Putting MapKit in Perspective.

    Here is an example of a snapshot with a .satelliteFlyover:

    enter image description here