SwiftUI点击Button跳转带Navigation Controller的UIKit ViewController
你可以根据实际交互需求选择模态弹出、导航栈Push两种实现路径,核心通过UIKit与SwiftUI的原生桥接能力实现,无需额外依赖。
方案1:模态弹出整个UIKit导航栈
适合不需要嵌入现有SwiftUI导航流、独立弹出UIKit业务模块的场景,实现成本最低:
- 先加一个通用扩展,让UIViewController可以直接作为SwiftUI视图内容使用
- 在Button的点击事件中通过
.sheet触发弹窗,弹窗内直接返回嵌入了目标VC的UINavigationController即可
完整可运行代码:
import SwiftUI // 通用扩展:让所有UIViewController可直接在SwiftUI中作为视图返回 extension UIViewController: UIViewControllerRepresentable { public func makeUIViewController(context: Context) -> UIViewController { self } public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} } // 你的目标UIKit业务页面,按常规UIKit写法实现即可 class TargetBusinessVC: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground title = "UIKit业务页" // 示例:添加关闭按钮 navigationItem.leftBarButtonItem = UIBarButtonItem( barButtonSystemItem: .close, target: self, action: #selector(dismissPage) ) } @objc private func dismissPage() { dismiss(animated: true) } } // SwiftUI调用页 struct HomePageView: View { @State private var showUIKitModule = false var body: some View { Button("打开UIKit模块") { showUIKitModule = true } .sheet(isPresented: $showUIKitModule) { // 把目标VC嵌入导航控制器后直接返回 UINavigationController(rootViewController: TargetBusinessVC()) .ignoresSafeArea() } } }
方案2:Push到现有SwiftUI导航栈
适合当前SwiftUI页面已经在NavigationStack/NavigationView导航栈内,需要把UIKit页面压入导航栈的场景。注意不要直接把UINavigationController塞进navigationDestination,会导致双导航栏冲突,需要通过桥接组件实现:
// 导航桥接组件:负责找到当前导航控制器执行push操作 struct UIKitPushBridge: UIViewControllerRepresentable { @Binding var isActive: Bool let buildTargetVC: () -> UIViewController func makeCoordinator() -> Coordinator { Coordinator(parent: self) } func makeUIViewController(context: Context) -> UIViewController { let anchorVC = UIViewController() anchorVC.view.backgroundColor = .clear return anchorVC } func updateUIViewController(_ anchorVC: UIViewController, context: Context) { guard isActive, let navController = anchorVC.navigationController else { return } // 避免重复push guard !(navController.topViewController?.isKind(of: TargetBusinessVC.self) ?? false) else { isActive = false return } navController.pushViewController(buildTargetVC(), animated: true) DispatchQueue.main.async { isActive = false } } class Coordinator: NSObject { let parent: UIKitPushBridge init(parent: UIKitPushBridge) { self.parent = parent } } } // 调用示例 struct HomePageView: View { @State private var pushToUIKitPage = false var body: some View { NavigationStack { Button("Push打开UIKit页面") { pushToUIKitPage = true } .background { UIKitPushBridge(isActive: $pushToUIKitPage) { // 这里返回你要push的目标UIKit VC,不需要额外包UINavigationController,会复用现有导航栈 TargetBusinessVC() } } .navigationTitle("SwiftUI首页") } } }
注意事项
- 模态弹出场景必须给UINavigationController加
.ignoresSafeArea(),否则导航栏会被安全区截断 - Push场景下目标VC不需要额外嵌套UINavigationController,会自动复用SwiftUI导航栈的导航控制器,避免双导航栏问题
- 如果点击按钮无响应,先检查
@State触发标记是否正确更新,确认当前页面没有其他正在展示的弹窗/sheet - 如果UIKit页面导航按钮点击无响应,检查按钮的
target/action是否正确绑定,避免循环引用导致VC提前释放
内容的提问来源于stack exchange,提问作者Mazharul Belal
相关产品推荐
相关产品推荐

