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...
- Get the photo from the buffer
- 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!
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 = self.imageView.bounds
let snapshot = MKMapSnapshotter(options: options)
snapshot.start { snapshot, error in
guard let snapshot = snapshot, error == nil else {
print("\(error)")
return
}
UIGraphicsBeginImageContextWithOptions(options.size, true, 0)
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) {
let pinCenterOffset = pinView.centerOffset
point.x -= pinView.bounds.size.width / 2
point.y -= pinView.bounds.size.height / 2
point.x += pinCenterOffset.x
point.y += pinCenterOffset.y
pinImage?.draw(at: point)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// do whatever you want with this image, e.g.
DispatchQueue.main.async {
self.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
:
I was getting an error from Rob's code that defines "rect" because my MKMapSnapshotter image is a UIImage. However .bounds is a property of UIImageView and not UIImage. So the rect definition throws an error.
Here is how I configured the options:
let mapSnapshotOptions = MKMapSnapshotOptions()
// Set the region of the map that is rendered.
let needleLocation = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
let region = MKCoordinateRegionMakeWithDistance(needleLocation, 1000, 1000)
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: 300, height: 300)
// Show buildings and Points of Interest on the snapshot
mapSnapshotOptions.showsBuildings = true
mapSnapshotOptions.showsPointsOfInterest = false
So is there another way to access the .bounds property? Or have I created my snapshot incorrectly?