'MKMapView inside NavigationView when clicking MKAnnotation is glitchy

Below is some code you can copy paste inside a new project in Xcode inside the ContentView.swift file. This is what I want:

  • A map with annotations
  • When an annotation is clicked, a NavigationView should redirect the user to a detailed screen
  • When the user navigates back to the map, the map is at the exact same spot as when the user clicked the annotation
  • The detailed screen has a property that MUST be initialized in the init method of the struct itself, no environment objects

I want to do this in MKMapView since I want to support clustering and overlays, something that is not available right now with the native SwiftUI Map.

The code that I have is glitchy. When the user navigates back to the map, the map is completely white for a second. After that second, the map is zoomed out so the user isn't at the exact same spot as before.

I did wrap the NavigationLink and MapView inside a ZStack but then the NavigationView does not animate when it is clicked.

This is the code:

import MapKit
import SwiftUI

struct AnnotationView: View {
    let annotation: Annotation

    var body: some View {
        Text("Not important")
    }
}

struct ContentView: View {
    @State private var selectedAnnotation: Annotation? = nil
    @State private var didSelectAny = false
    var body: some View {
        NavigationView {
            Group {
                if didSelectAny {
                    if let annotation = selectedAnnotation {
                        NavigationLink(destination: AnnotationView(annotation: annotation), isActive: $didSelectAny) {}
                    }
                }
                MapView { annotation in
                    selectedAnnotation = annotation
                    didSelectAny = true
                }
            }.navigationBarTitle("Map")
        }
        .navigationViewStyle(.stack)
    }
}

class MapViewCoordinator: NSObject, MKMapViewDelegate {
    var mapViewController: MapView

    init(_ control: MapView) {
        mapViewController = control
    }

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let annotation = annotation as? Annotation else { return nil }

        let identifier = "Annotation"
        var annotationView = mapView
            .dequeueReusableAnnotationView(withIdentifier: identifier)

        if annotationView == nil {
            annotationView = MKAnnotationView(
                annotation: annotation,
                reuseIdentifier: identifier
            )
        } else {
            annotationView!.annotation = annotation
        }

        annotationView!.image = UIImage(systemName: "person.2")!

        return annotationView
    }

    func mapView(_ map: MKMapView, didSelect view: MKAnnotationView) {
        guard
            let annotationView = view.annotation,
            let annotation = annotationView as? Annotation
        else {
            // Could be current users location
            return
        }

        annotation.didSelect(annotation)

        map.deselectAnnotation(annotationView, animated: false)
    }
}

struct MapView: UIViewRepresentable {
    let didSelect: (Annotation) -> Void

    func makeUIView(context _: Context) -> MKMapView {
        MKMapView()
    }

    func makeCoordinator() -> MapViewCoordinator {
        MapViewCoordinator(self)
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        let center = CLLocationCoordinate2D(latitude: 10, longitude: 10)
        view.delegate = context.coordinator
        view.region = .init(center: center, span: .init(latitudeDelta: 0.1, longitudeDelta: 0.1))

        let annotation = Annotation(didSelect: didSelect)

        annotation.coordinate = center

        view.addAnnotation(annotation)
    }
}

class Annotation: MKPointAnnotation {
    let didSelect: (Annotation) -> Void

    init(
        didSelect: @escaping (Annotation) -> Void
    ) {
        self.didSelect = didSelect

        super.init()

        title = "title"
    }
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source