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()
}
}
Definitely not possible, SwiftUI Map does not yet support overlays. Using UIViewRepresentsable is easy, see SO example here:
Swiftui how to use MKOverlayRenderer? using the following code:
struct MapView: UIViewRepresentable {
@Binding var route: MKPolyline?
let mapViewDelegate = MapViewDelegate()
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
view.delegate = mapViewDelegate // (1) This should be set in makeUIView, but it is getting reset to `nil`
view.translatesAutoresizingMaskIntoConstraints = false // (2) In the absence of this, we get constraints error on rotation; and again, it seems one should do this in makeUIView, but has to be here
addRoute(to: view)
}
}
private extension MapView {
func addRoute(to view: MKMapView) {
if !view.overlays.isEmpty {
view.removeOverlays(view.overlays)
}
guard let route = route else { return }
let mapRect = route.boundingMapRect
view.setVisibleMapRect(mapRect, edgePadding: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10), animated: true)
view.addOverlay(route)
}
}
class MapViewDelegate: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.fillColor = UIColor.red.withAlphaComponent(0.5)
renderer.strokeColor = UIColor.red.withAlphaComponent(0.8)
return renderer
}
}
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:)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.