2

I have mapView with array of CLLocationCoordinate2D. I use these locations to draw lines on my mapView by using MKPolyline. Now i want to store it as a UIimage. I found that theres class MKMapSnapshotter but unfortunately i can't draw overlays on it "Snapshotter objects do not capture the visual representations of any overlays or annotations that your app creates." So i get only blank map image. Is there any way to get image with my overlays?

private func generateImageFromMap() {
    let mapSnapshotterOptions = MKMapSnapshotter.Options()
    guard let region = mapRegion() else { return }
    mapSnapshotterOptions.region = region
    mapSnapshotterOptions.size = CGSize(width: 200, height: 200)
    mapSnapshotterOptions.showsBuildings = false
    mapSnapshotterOptions.showsPointsOfInterest = false


    let snapShotter = MKMapSnapshotter(options: mapSnapshotterOptions)
    snapShotter.start() { snapshot, error in
        guard let snapshot = snapshot else {
//do something with image .... 
            let mapImage = snapshot...
        }
    }
}

How can i put overlays on this image? Or maybe theres other way for that problem.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
ShadeToD
  • 403
  • 9
  • 22

1 Answers1

2

Unfortunately, you have to draw them yourself. Fortunately, MKSnapshot has a convenient point(for:) method to convert a CLLocationCoordinate2D into a CGPoint within the snapshot.

For example, assume you had an array of CLLocationCoordinate2D:

private var coordinates: [CLLocationCoordinate2D]?

private func generateImageFromMap() {
    guard let region = mapRegion() else { return }

    let options = MKMapSnapshotter.Options()
    options.region = region
    options.size = CGSize(width: 200, height: 200)
    options.showsBuildings = false
    options.showsPointsOfInterest = false

    MKMapSnapshotter(options: options).start() { snapshot, error in
        guard let snapshot = snapshot else { return }

        let mapImage = snapshot.image

        let finalImage = UIGraphicsImageRenderer(size: mapImage.size).image { _ in

            // draw the map image

            mapImage.draw(at: .zero)

            // only bother with the following if we have a path with two or more coordinates

            guard let coordinates = self.coordinates, coordinates.count > 1 else { return }

            // convert the `[CLLocationCoordinate2D]` into a `[CGPoint]`

            let points = coordinates.map { coordinate in
                snapshot.point(for: coordinate)
            }

            // build a bezier path using that `[CGPoint]`

            let path = UIBezierPath()
            path.move(to: points[0])

            for point in points.dropFirst() {
                path.addLine(to: point)
            }

            // stroke it

            path.lineWidth = 1
            UIColor.blue.setStroke()
            path.stroke()
        }

        // do something with finalImage
    }
}

Then the following map view (with the coordinates, as MKPolyline, rendered by mapView(_:rendererFor:), like usual):

enter image description here

The above code will create the this finalImage:

enter image description here

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • No idea why but didint work with private var coordinates: [CLLocationCoordinate2D]? for me.I couldnt append any 2Ccoordinates to this array. I change it to var coord2d: [CLLocationCoordinate2D] = [] and changed guard let to let coordinates = self.coord2d if self.coord2d.count > 1 { } else { return }. And it works perfectly! Thats what i was looking for – ShadeToD Feb 14 '19 at 07:12
  • No problem. I’m glad this solved your issue. FWIW, the use of that private variable is unrelated to the question here. Clearly, I assumed you’d use your own, existing array of coordinates. But you didn’t share how those coordinates were stored in your code, so I had to use something, and I had no way of knowing what you called your array. Lol. – Rob Feb 15 '19 at 01:41