Navigating to a SwiftUI view from a button pressed in a UIViewControllerRepresentable

783 Views Asked by At

I am using a Swiftui list to show tasks. Each task has a location. When you click on the list row I am using a NavigationLink to navigate to a details page.

I am using a UIViewControllerRepresentable that creates a map view with all the task locations annotated on the map. I had to use UIKit so that you can interact with the annotations. I have set up a delegate that fires when the user clicks the map annotation accessory.

The aim: when the delegate function is called I want to pass back the taskid which is used to navigate to the details page like when the list row is clicked on:

Chevron right calls delegate

List View

The way I am Navigating from the list:

ForEach(tasksToDisplayInList, id: \.id) { task in
    NavigationLink(
        destination: TaskDetailsView(taskId: task.id, title: task.title)) 
    {
        TaskListRow(task: task)
            .padding(.trailing, 5)
            .foregroundColor(.textDefault)
    }
}

The Task List Map UIViewControllerRepresentable that passes back the pressed task id:

struct TaskListMap: UIViewControllerRepresentable {
    typealias UIViewControllerType = TaskListMapController
    
    @Binding var annotations: [TaskAnnotation]
    
    class Coordinator: TaskListMapDelegate {
        func didPressMoreDetail(_ mapController: TaskListMapController, for annotation: TaskAnnotation) {
            // TaskAnnotation contains the taskId
            // Navigate here somehow?
        }
        
        var parent: TaskListMap
        
        init(_ parent: TaskListMap) {
            self.parent = parent
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> TaskListMapController {
        let mapController = TaskListMapController(annotations: annotations)
        mapController.delegate = context.coordinator
        return mapController
    }
    
    func updateUIViewController(_ uiViewController: TaskListMapController, context: Context) {
        if annotations.count != uiViewController.mapView.annotations.count {
            uiViewController.mapView.removeAnnotations(uiViewController.mapView.annotations)
            uiViewController.mapView.addAnnotations(annotations)
        }
    }
}
1

There are 1 best solutions below

0
On

As a workaround, this seems to work fine for my purpose:

// # TaskListMapView.swift (inside the coordinator)
    
class Coordinator: TaskListMapDelegate {
    var parent: TaskListMap
        
    init(_ parent: TaskListMap) {
        self.parent = parent
    }

    func didPressMoreDetail(_ mapController: TaskListMapController, for annotation: TaskAnnotation) {
            
        // Push the navigation with a hosting controller

        let detailsView = UIHostingController(rootView: TaskDetailsView(taskId: annotation.taskId, title: annotation.title ?? ""))

        mapController.navigationController?.pushViewController(detailsView, animated: true)

     }
}