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

如何在UIHostingController的SwiftUI视图中调用pushViewController跳转

在SwiftUI视图中调用UIKit ViewController的导航方法

问题描述

我有一个UIKit应用,通过UIHostingController展示SwiftUI视图。该视图内有一个按钮,希望点击它导航至另一个ViewController。请问如何在此SwiftUI视图内部push新的ViewController?(或更宽泛地:如何从SwiftUI视图内部调用ViewController的函数?)

当然我可以在SwiftUI中使用NavigationView,但为了实现业务逻辑与UI的清晰分离,我希望大部分逻辑用UIKit实现。

原始代码如下:

MainViewController代码

import UIKit
import SwiftUI

class MainViewController: UIViewController {

    var viewModel = ViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        addView()
    }
    
    func addView() {
            let testView = SwiftUIView(viewModel: viewModel)
            let controller = UIHostingController(rootView: testView)
            addChild(controller)
            controller.view.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(controller.view)
            controller.didMove(toParent: self)

            NSLayoutConstraint.activate([
                controller.view.widthAnchor.constraint(equalTo: view.widthAnchor),
                controller.view.heightAnchor.constraint(equalTo: view.heightAnchor),
                controller.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                controller.view.centerYAnchor.constraint(equalTo: view.centerYAnchor)
            ])
        }

    func navigate() { // 如何从SwiftUI视图中调用这个方法?
        let otherView = Text("test")
        let nextVc = UIHostingController(rootView: otherView)
        navigationController?.pushViewController(nextVc, animated: true)
    }
}

SwiftUI视图代码

import SwiftUI

struct SwiftUIView: View {
    
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
         Button {
                    // 在这里触发push操作
                } label: {
                    Text("go")
                }
    }
}

解决方案

方法一:通过ViewModel传递闭包(推荐)

利用ViewModel作为中间层,在ViewModel中定义闭包属性,将UIKit的导航逻辑绑定到闭包上,SwiftUI视图只需调用该闭包即可触发导航。这种方式既保持了UI与业务逻辑的分离,又实现了跨框架的交互。

步骤1:更新ViewModel

添加用于触发导航的闭包属性:

import Foundation

class ViewModel: ObservableObject {
    // 定义导航闭包,可选类型避免空值问题
    var navigateAction: (() -> Void)?
}

步骤2:在MainViewController中绑定闭包

在viewDidLoad中为ViewModel的闭包赋值,关联到navigate方法:

override func viewDidLoad() {
    super.viewDidLoad()
    addView()
    // 绑定导航逻辑到ViewModel的闭包,使用weak self避免循环引用
    viewModel.navigateAction = { [weak self] in
        self?.navigate()
    }
}

步骤3:在SwiftUI视图中调用闭包

修改按钮的点击事件,调用ViewModel中的闭包:

struct SwiftUIView: View {
    
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
         Button {
             // 调用闭包触发导航
             viewModel.navigateAction?()
         } label: {
             Text("go")
         }
    }
}

方法二:直接传递ViewController引用(不推荐)

如果不需要严格的解耦,可以直接将MainViewController的弱引用传递给SwiftUI视图,直接调用其方法。但这种方式会增加SwiftUI与UIKit的耦合度,不利于后续维护。

步骤1:修改SwiftUI视图,添加ViewController引用

struct SwiftUIView: View {
    
    @ObservedObject var viewModel: ViewModel
    // 使用weak引用避免循环引用
    weak var parentViewController: MainViewController?
    
    var body: some View {
         Button {
             parentViewController?.navigate()
         } label: {
             Text("go")
         }
    }
}

步骤2:在MainViewController中传递引用

func addView() {
        // 创建SwiftUI视图时传入当前ViewController的引用
        let testView = SwiftUIView(viewModel: viewModel, parentViewController: self)
        let controller = UIHostingController(rootView: testView)
        // ... 其余代码不变
}

总结

方法一通过ViewModel传递闭包,符合MVVM设计思想,实现了低耦合,是更优的方案;方法二更直接,但耦合度高,仅适用于简单场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 22:12:22