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

SwiftUI中UIViewControllerRepresentable的导航跳转方案咨询

解决SwiftUI中UICollectionView单元格跳转导航问题

你的问题核心在于试图在UIKit的Coordinator里直接操作导航控制器,但实际上SwiftUI的NavigationView和UIKit的UINavigationController属于不同层级,直接创建新导航控制器会导致嵌套问题,也不符合SwiftUI的导航逻辑。最优方案是让UIKit部分只负责传递点击事件,把导航逻辑交还给SwiftUI处理。

优化方案步骤:

  • 给CollectionView添加回调闭包,用于传递选中的商品信息
  • 在ExploreView中利用SwiftUI的NavigationLink处理跳转逻辑
  • 修改Coordinator,在cell点击时触发回调,不再手动创建导航控制器

修改后的完整代码:

1. 更新CollectionView封装类

import SwiftUI
import SDWebImage

struct CollectionView: UIViewControllerRepresentable {
    var products: [Product]
    // 添加回调闭包,传递选中的Product
    var onProductSelected: ((Product) -> Void)?
    let cellId = "cellId"
    typealias UIViewControllerType = UICollectionViewController
    
    func makeUIViewController(context: Context) -> UICollectionViewController {
        let collectionView = UICollectionViewController(collectionViewLayout: createLayout())
        collectionView.collectionView.backgroundColor = .white
        collectionView.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
        collectionView.collectionView.dataSource = context.coordinator
        collectionView.collectionView.delegate = context.coordinator
        return collectionView
    }
    
    func createLayout() -> UICollectionViewCompositionalLayout {
        return UICollectionViewCompositionalLayout { sectionNumber, env in
            let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(0.3333), heightDimension: .fractionalWidth(0.3333)))
            item.contentInsets.trailing = 1
            item.contentInsets.bottom = 1
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .estimated(500)), subitems: [item])
            group.contentInsets.leading = 1
            let section = NSCollectionLayoutSection(group: group)
            return section
        }
    }
    
    func updateUIViewController(_ uiViewController: UICollectionViewController, context: Context) {
    }
    
    class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return parent.products.count
        }
        
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: parent.cellId, for: indexPath)
            let imageView = UIImageView()
            imageView.contentMode = .scaleAspectFill
            imageView.layer.masksToBounds = true
            imageView.sd_setImage(with: URL(string: parent.products[indexPath.item].imageUrl)) { (image, _, _, _) in
                imageView.image = image
            }
            cell.backgroundView = imageView
            return cell
        }
        
        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            // 触发回调,传递选中的商品
            let selectedProduct = parent.products[indexPath.item]
            parent.onProductSelected?(selectedProduct)
        }
        
        let parent: CollectionView
        init(_ parent: CollectionView) {
            self.parent = parent
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}

2. 更新ExploreView主视图

import SwiftUI

struct ExploreView: View {
    @ObservedObject var exploreViewVM = ExploreViewViewModel()
    // 用于控制NavigationLink的激活状态
    @State private var selectedProduct: Product?
    
    var body: some View {
        NavigationView {
            ZStack {
                CollectionView(products: exploreViewVM.products) { product in
                    // 收到点击回调,设置选中商品
                    selectedProduct = product
                }
                
                // 隐藏的NavigationLink,用于触发跳转
                NavigationLink(
                    destination: ProductView(product: selectedProduct!),
                    isActive: Binding(
                        get: { selectedProduct != nil },
                        set: { if !$0 { selectedProduct = nil } }
                    ),
                    label: { EmptyView() }
                )
            }
            .navigationTitle("Explore")
        }
    }
}

struct ExploreView_Previews: PreviewProvider {
    static var previews: some View {
        ExploreView()
    }
}

方案优势:

  • 符合SwiftUI设计逻辑:UIKit视图仅作为展示层,导航逻辑交还给SwiftUI处理
  • 避免嵌套导航控制器:不会出现多个UINavigationController嵌套的混乱问题
  • 代码解耦:CollectionView不需要关心导航跳转的具体实现,只负责传递点击事件

补充说明:

如果偏好模态跳转,可将NavigationLink替换为.sheet修饰符:

.sheet(item: $selectedProduct) { product in
    ProductView(product: product)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 09:32:28