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.