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
相关产品推荐
相关产品推荐

