You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI:如何从UIViewRepresentable的UITableView导航至View

解决UITableView选中行后在NavigationView中跳转的问题

你遇到的核心问题是:在UIViewRepresentable封装的UITableView的Coordinator里,无法直接访问到NavigationView对应的UINavigationController。下面提供两种可行的解决方案,分别适配SwiftUI风格和UIKit风格的跳转需求:


方案一:用SwiftUI原生NavigationLink(推荐)

这种方式更贴合SwiftUI的状态驱动设计,通过在TableView中定义选中回调,在ContentView里用NavigationLink完成跳转:

步骤1:给TableView添加选中回调闭包

修改TableView结构体,添加一个@escaping闭包来传递选中事件:

struct TableView: UIViewRepresentable {
    @State var rows = ["London", "Paris", "Oslo"]
    // 新增:选中行的回调闭包
    let onRowSelected: (Int) -> Void

    func makeUIView(context: Context) -> UITableView {
        let table = UITableView()
        table.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        table.dataSource = context.coordinator
        table.delegate = context.coordinator
        return table
    }

    func updateUIView(_ uiView: UITableView, context: Context) {}

    func makeCoordinator() -> Coordinator {
        // 初始化Coordinator时传递回调闭包
        return Coordinator(rows: $rows, onRowSelected: onRowSelected)
    }

    class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
        @Binding var rows: [String]
        let onRowSelected: (Int) -> Void

        init(rows: Binding<[String]>, onRowSelected: @escaping (Int) -> Void) {
            self._rows = rows
            self.onRowSelected = onRowSelected
        }

        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return self.rows.count
        }

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
            cell?.textLabel?.text = self.rows[indexPath.row]
            return cell!
        }

        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            // 触发回调,传递选中的行索引
            onRowSelected(indexPath.row)
        }

        // 保留你的左右滑操作代码...
        func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
            let add = UIContextualAction(style: .normal,title: "Add") { (action, view, success) in
                success(true)
            }
            add.backgroundColor = .gray
            return UISwipeActionsConfiguration(actions: [add])
        }

        func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
            let remove = UIContextualAction(style: .normal,title: "Remove") { (action, view, success) in
                success(true)
            }
            remove.backgroundColor = .red
            let edit = UIContextualAction(style: .normal,title: "Edit") { (action, view, success) in
                success(true)
            }
            edit.backgroundColor = .gray
            let color = UIContextualAction(style: .normal, title: "Color") { (action, view, success) in
                success(true)
            }
            return UISwipeActionsConfiguration(actions: [remove, edit, color])
        }
    }
}

步骤2:在ContentView中使用NavigationLink响应跳转

通过状态变量控制隐藏的NavigationLink,实现选中行后的跳转:

struct ContentView: View {
    // 存储选中的行索引,控制导航状态
    @State private var selectedRowIndex: Int? = nil

    var body: some View {
        NavigationView {
            VStack {
                HStack {
                    Text("Some other Views")
                }
                // 传递选中回调
                TableView(onRowSelected: { index in
                    selectedRowIndex = index
                })

                // 隐藏的NavigationLink,通过状态触发跳转
                NavigationLink(
                    destination: Text("Destination for \(rows[selectedRowIndex ?? 0])"),
                    tag: selectedRowIndex,
                    selection: $selectedRowIndex
                ) {
                    EmptyView()
                }
                .hidden()
            }
            .navigationTitle("Cities")
        }
    }

    // 复用TableView的数据源,或者直接传递也行
    private let rows = ["London", "Paris", "Oslo"]
}

方案二:直接获取UINavigationController(UIKit风格)

如果你更习惯用UIKit的pushViewController方式,可以通过UIView的扩展获取父视图控制器,进而拿到UINavigationController:

步骤1:添加UIView扩展获取父ViewController

extension UIView {
    // 获取当前视图所在的父ViewController
    var parentViewController: UIViewController? {
        var currentResponder: UIResponder? = self
        while currentResponder != nil {
            currentResponder = currentResponder?.next
            if let vc = currentResponder as? UIViewController {
                return vc
            }
        }
        return nil
    }
}

步骤2:在Coordinator的didSelectRowAt中跳转

修改Coordinator中的didSelectRowAt方法:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let destination = Text("Destination for \(rows[indexPath.row])")
    let host = UIHostingController(rootView: destination)
    
    // 获取当前NavigationController并跳转
    if let navController = tableView.parentViewController?.navigationController {
        navController.pushViewController(host, animated: true)
    }
}

这种方式不需要修改TableView的外部接口,但依赖UIKit的视图层级,灵活性不如方案一。


内容的提问来源于stack exchange,提问作者mahan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:28:26