I try to create Map view with annotations and overlays using only iOS 14 SwiftUI Map. I managed to create annotations but I am not able to create visible overlays because of lack of MKOverlayRenderer from MKMapView delegate. How can I attach to Map view delegate so it can run its method
Map(coordinateRegion: $region, showsUserLocation: true, userTrackingMode: $userTrackingMode, annotationItems: annotations)) { annotation in
MapAnnotation(coordinate: CLLocationCoordinate2DMake(annotation.latitude, annotation.longitude),
anchorPoint: CGPoint(x: 0.5, y: 0.7)) {
NavigationLink(destination: DetailsView()) {
VStack {
Image(systemName: "mappin")
.font(.title)
.foregroundColor(.red)
Text(annotation.name.split(separator: " ").joined(separator: "\n"))
.font(.caption)
.bold()
.multilineTextAlignment(.center)
.foregroundColor(.black)
.padding(2)
.background(RoundedRectangle(cornerRadius: 4).fill(Color.white.opacity(0.8)))
.layoutPriority(1)
}
}
}
}
.addOverlays(mapOverlays, delegate: mapDelegate)
and Map extension method:
extension Map {
func addOverlays(_ overlays: [MKOverlay], delegate: MKMapViewDelegate) -> some View {
// MKMapView.appearance().delegate = delegate // THIS DOES NOT WORK AT ALL
MKMapView.appearance().addOverlays(overlays)
return self
}
}
and delegate:
class MapDelegate: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKCircle {
let circle = MKCircleRenderer(overlay: overlay)
circle.strokeColor = .systemRed
circle.fillColor = .systemRed
circle.alpha = 0.2
circle.lineWidth = 1
return circle
}
return MKOverlayRenderer()
}
}
Probably this is a pretty late answer, but could be useful to someone else.
After trying a few approaches, I came to this solution (btw, I would say that's not even close to a fancy code... but it works).
First, you have to create a class that implements the MKMapViewDelegate (in my case I needed to show an overlay with a custom theme on the Map, but you can use it to show a Polyline or a Polygon as well).
class MapCustomDelegate: NSObject, MKMapViewDelegate {
var parent: MKMapView
init(_ parent: MKMapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let tileOverlay = overlay as? MKTileOverlay {
let renderer = MKTileOverlayRenderer(overlay: tileOverlay)
return renderer
}
return MKOverlayRenderer()
}
}
Then in the view you have to implement the delegate using the MKMapView.appearance() instance, like this:
struct MapCustomView: View {
// Properties
@StateObject private var viewModel: MapViewModel = MapViewModel()
private let mapAppearanceInstance = MKMapView.appearance()
private var mapCustomDelegate: MapCustomDelegate = MapCustomDelegate(MKMapView.appearance())
..
var body: some View {
VStack {
Map(coordinateRegion: $viewModel.mapRegion,
annotationItems: viewModel.items) { item in
MapAnnotation(coordinate: item.location) {
..
}
}
.onAppear {
// viewModel.mapOverlay -> MKTileOverlay
self.mapAppearanceInstance.delegate = mapCustomDelegate
self.mapAppearanceInstance.addOverlay(viewModel.mapOverlay)
}
}
}
And it should work. Hope this helps :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With