0

Using SwiftUI, I have a List embedded in a Navigation view, when a user selects a row a new Map View is created via a NavigationLink.

In this new Mapview, a user can add a pin on the map and select done via a trailing button (green airplane button) in the NavigationTabBar

The UI is working as it should. My issue is that after a user has added a pin on the map, I would like to pass back does coordinates to its root view, so I can trigger the function call

item.createLocation(item.id, ***coordinates from callback***)

struct RootView: View {
    @StateObject private var data = Model()
    var body: some View {
        NavigationView{
            List(data.entities) {item in
                NavigationLink(destination:RowView(entity: item.content)){
                    Text("Select this row \(item.id)")
                }
            }.navigationTitle("Long press on the map to add a pin")
            .navigationBarItems(
                trailing:
                    Button(action: {
                        item.createLocation(item.id, selectedLocation),
                        self.isActive = false
                        })
                    }) {
                        Image(systemName: "paperplane.circle.fill")
                            .font(.title)
                            .foregroundColor(.green)
                    })
        }
    }
}

Here is the child view (MapView):

var selectedLocation = CLLocationCoordinate2D()

struct RowView: UIViewRepresentable {
    let location: Location
    var locationManager = CLLocationManager()
    let locationHandler = LocationHandler()

    public func makeUIView(context:Context)-> MKMapView{
        let mapView = MKMapView(frame: .zero)
        mapView.delegate = context.coordinator
        let longPressed = UILongPressGestureRecognizer(target: context.coordinator,
                                                       action:#selector(context.coordinator.addPinBasedOnGesture(_:)))
        longPressed.minimumPressDuration = 1.0
        mapView.addGestureRecognizer(longPressed)
        
        return mapView
    }
    
    /**
     * Centers map based on location
     * @param view as MKMapView, the map view
     * @param location as CLLocation, Location data that includes coordinates
     */
    func centerToLocation(_ view:MKMapView, _ location: CLLocation?){
        if let location = location {
            let coordinate = CLLocationCoordinate2D(latitude:location.coordinate.latitude ,longitude: location.coordinate.longitude)
                    view.setRegion(MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000), animated: true)
        }
    }
    
    func updateUIView(_ view:MKMapView, context:Context){
        if locationHandler.authorized {
            view.showsUserLocation = true
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, MKMapViewDelegate{
        var parent: LocationDetailView
        var annotationCoords:CGPoint = CGPoint(x: 0.0, y: 0.0)

        init(_ parent: LocationDetailView) {
            self.parent = parent
        }

        @objc func addPinBasedOnGesture(_ gestureRecognizer:UILongPressGestureRecognizer) {
            let mapview = (gestureRecognizer.view as? MKMapView)
            mapview?.removeAnnotations(mapview!.annotations)
            annotationCoords = gestureRecognizer.location(in: mapview)
            let newCoordinates = mapview?.convert(annotationCoords, toCoordinateFrom: mapview)
            let annotation = MKPointAnnotation()
            guard let _newCoordinates = newCoordinates else { return }
            annotation.coordinate = _newCoordinates
            mapview?.addAnnotation(annotation)
            selectedLocation = _newCoordinates
        }
    }
}

I am saving the coordinates of the map pin into the variable selectedLocation, which is setup as a global variable.

The main issue is that when pressing the Button(action: { item.createLocation(item.id, selectedLocation)) the item.id is always different and does not return the item.id of that selected row.

In UIKit I would implement a delegate to handle this, but I am reading that using delegates are not best practice in SwiftUI.

My question is, how do I trigger a callback from a second child of a NavigationView in SwiftUI.

PhilBlais
  • 1,076
  • 4
  • 13
  • 36

0 Answers0