Hi there,
I'm trying to make Map with Annotations that link to a new full page view created in SwiftUI. For whatever reason, I cannot get anything inside CustomAnnotationView to handle a tap event. NavigationLinks, nor .onTapGestures work inside that view, and I've been stuck on this for quite a while. Anyone know what I'm doing wrong?
(sorry the code probably has a lot of unnecessary bluff whilst I've been trying to make this work)
import SwiftUI
import MapKit
class Annotation: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let station: Station
init(_ station: Station) {
self.coordinate = station.location.coordinates
self.station = station
}
}
class AnnotationView: MKAnnotationView {
private var hostingController: UIHostingController<AnyView>?
override var annotation: MKAnnotation? {
didSet {
if let annotation = annotation as? Annotation {
let customView = CustomAnnotationView(data: annotation.station)
let hostingController = UIHostingController(rootView: AnyView(customView))
self.hostingController = hostingController
self.addSubview(hostingController.view)
self.isEnabled = false
}
}
}
override var isUserInteractionEnabled: Bool {
didSet {
super.isUserInteractionEnabled = false
}
}
override func prepareForReuse() {
super.prepareForReuse()
hostingController?.view.removeFromSuperview()
hostingController = nil
}
}
struct CustomAnnotationView: View {
let data: Station
var body: some View {
NavigationLink {
StationDetailView(station: .constant(data))
} label: {
FuelCapsule(station: .constant(data))
}
}
}
struct MapView: UIViewRepresentable {
@Binding var region: MKCoordinateRegion
@Binding var annotations: [Station]
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let mapView = MKMapView()
mapView.setRegion(region, animated: false)
mapView.delegate = context.coordinator
mapView.showsCompass = false
mapView.isPitchEnabled = false
mapView.isRotateEnabled = false
mapView.showsUserLocation = true
mapView.register(AnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.isUserInteractionEnabled = true
return mapView
}
func centerRegion(_ uiView: MKMapView, coordinate: CLLocationCoordinate2D, context: Context) {
uiView.setCenter(coordinate, animated: true)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
var oldAnnotations = [Int: Annotation]()
uiView.annotations.compactMap { $0 as? Annotation }.forEach { oldAnnotations[$0.station.id] = $0 }
var newAnnotations = [Int: Annotation]()
annotations.map(Annotation.init).forEach { newAnnotations[$0.station.id] = $0 }
let annotationsToRemove = oldAnnotations.filter { !newAnnotations.keys.contains($0.key) }.values
let annotationsToAdd = newAnnotations.filter { !oldAnnotations.keys.contains($0.key) }.values
uiView.removeAnnotations(Array(annotationsToRemove))
uiView.addAnnotations(Array(annotationsToAdd))
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
DispatchQueue.main.async {
self.parent.region = mapView.region
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? Annotation else { return nil }
let annotationView = AnnotationView(annotation: annotation, reuseIdentifier: "customView")
annotationView.canShowCallout = false
return annotationView
}
internal func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if view.annotation is MKUserLocation {
mapView.deselectAnnotation(view.annotation, animated: false)
return
}
}
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView(region: .constant(MKCoordinateRegion(center: MapDetails.startingLocation, span: MapDetails.defaultSpan)), annotations: .constant([]))
}
}