UIKit项目中通过UIHostingController实现SwiftUI页面导航的正确方案
为什么会出现双导航栏?
你的UIKit应用已经由原生UINavigationController提供导航栏,而SwiftUI的NavigationView是独立的导航容器,会创建自己的导航栏结构。当你把SwiftUI视图嵌套在NavigationView中再放入UIHostingController时,就会形成UIKit导航栏 + SwiftUI导航栏的嵌套结构,最终显示双标题。
两种设置标题方式的原理
展示前设置UIHostingController的导航标题:
UIHostingController是UIViewController的子类,当它被push到UIKit的UINavigationController栈中时,系统会使用它的navigationItem渲染导航栏内容。直接设置hostingVC.navigationItem.title,本质就是操作UIKit原生导航栏元素,和普通UIKit页面的标题设置逻辑完全一致。SwiftUI的
navigationTitle修饰符:
当SwiftUI视图没有嵌套NavigationView时,UIHostingController的桥接层会自动把SwiftUI中的导航相关修饰符(比如navigationTitle、navigationBarItems)映射到自身的navigationItem上。相当于SwiftUI通过底层桥接直接操作UIKit导航栏,所以和直接设置navigationItem的效果完全相同。
推荐的导航方式(无需自定义UIKit导航方案)
方式一:复用UIKit导航栈(首选)
直接在SwiftUI视图中使用NavigationLink,不需要嵌套NavigationView。此时SwiftUI会通过桥接层自动将目标SwiftUI视图包装成UIHostingController,并push到现有UIKit的UINavigationController栈中,完全复用原生导航逻辑。
示例代码:
// SwiftUI视图 struct FirstSwiftUIView: View { var body: some View { VStack(spacing: 20) { Text("这是SwiftUI页面") // 直接使用NavigationLink跳转到另一个SwiftUI页面 NavigationLink("前往第二个SwiftUI页面") { SecondSwiftUIView() } } .navigationTitle("SwiftUI首页") // 自动映射到UIKit导航栏标题 } } // UIKit侧展示代码(比如在某个UIViewController中) let hostingVC = UIHostingController(rootView: FirstSwiftUIView()) navigationController?.pushViewController(hostingVC, animated: true)
这种方式的优势是完全保持UIKit原生的导航体验,返回按钮、导航栏样式都和现有应用一致,不需要额外适配。
方式二:SwiftUI内部独立导航(仅特殊场景使用)
如果你的SwiftUI页面需要独立的导航栈(比如实现分栏导航等SwiftUI专属特性),可以嵌套NavigationView,但必须隐藏UIKit的导航栏避免冲突:
示例代码:
// SwiftUI视图 struct SwiftUINavContainerView: View { var body: some View { NavigationView { List { NavigationLink("子页面1", destination: ChildSwiftUIView()) NavigationLink("子页面2", destination: AnotherChildView()) } .navigationTitle("SwiftUI内部导航") } } } // UIKit侧展示代码 let hostingVC = UIHostingController(rootView: SwiftUINavContainerView()) // 隐藏UIKit的导航栏 navigationController?.setNavigationBarHidden(true, animated: true) navigationController?.pushViewController(hostingVC, animated: true)
注意:这种方式会让SwiftUI的导航栈和UIKit的栈完全分离,返回逻辑、导航栏样式都由SwiftUI控制,可能和现有UIKit页面的体验不一致,仅在必要时使用。
总结
不需要构建自定义UIKit导航方案,优先选择复用现有UIKit导航栈的方式:SwiftUI视图不嵌套NavigationView,直接用NavigationLink跳转,通过navigationTitle设置标题即可。这种方式既能利用SwiftUI的视图开发效率,又能保持应用导航体验的一致性。
内容的提问来源于stack exchange,提问作者Tabonx

