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

如何从UIKit视图控制器向SwiftUI视图传递EnvironmentObject

从UIKit控制SwiftUI视图的状态:方案详解

问题描述

我有一个使用EnvironmentObject更新标签内容的SwiftUI项目,同时通过UIViewControllerRepresentable协议嵌入了一个UIKit的UIViewController。希望通过UIKit代码控制SwiftUI视图中的标签,请问能否从UIKit代码中传递EnvironmentObject?如果不可行,从UIKit视图向SwiftUI视图传递数据的理想方案是什么?

核心结论

可以通过共享ObservableObject实例的方式实现UIKit到SwiftUI的状态同步,不需要直接传递EnvironmentObject(EnvironmentObject本质是注入到环境中的ObservableObject,我们只需把同一个实例传给UIKit即可)。这是最符合SwiftUI状态管理理念的方案。

具体实现步骤

1. 修改UIViewControllerRepresentable,传递ObservableObject实例

更新MyUIView.swift,让它接收Options实例作为参数,并在创建MyUIViewController时传递过去:

import SwiftUI

struct MyUIView: UIViewControllerRepresentable {
    // 接收Options实例
    let options: Options
    
    func makeUIViewController(context: Context) -> MyUIViewController {
        let vc = MyUIViewController()
        // 将实例传给UIViewController
        vc.options = options
        return vc
    }
    
    func updateUIViewController(_ uiViewController: MyUIViewController, context: Context) {
        // 若需要从SwiftUI同步状态到UIKit,可在此处理
    }
}

2. 为UIViewController添加ObservableObject属性

更新MyUIViewController.swift,添加options属性,并在按钮点击时修改其值:

import UIKit

class MyUIViewController: UIViewController {
    // 声明Options属性,接收从SwiftUI传来的实例
    var options: Options!
    var btn: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        var config = UIButton.Configuration.filled()
        config.title = "Change Option"
        
        btn = UIButton(configuration: config)
        btn.addAction(UIAction(handler: { [weak self] _ in
            // 修改Options的属性,触发SwiftUI视图更新
            self?.options.option = 0
        }), for: .touchUpInside);
        
        btn.frame = CGRect(x: 0, y: 0, width: 100, height: 50)
        btn.center = self.view.center // 调整按钮位置到视图中心
        self.view.addSubview(btn)
    }
}

3. 在SwiftUI视图中传递ObservableObject实例

更新ContentView.swift,将EnvironmentObject的options实例传给MyUIView:

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var options: Options
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            
            if options.option == 0 {
                Text("Hello, world!")
            } else {
                Text("Other Options")
            }
            
            // 传递options实例给MyUIView
            MyUIView(options: options)
        }
        .padding()
    }
}

#Preview {
    ContentView().environmentObject(Options())
}

方案原理

  • Options是被@MainActor修饰的ObservableObject,其@Published属性的修改会自动触发所有依赖该实例的SwiftUI视图更新。
  • 通过将同一个Options实例同时注入SwiftUI环境和传递给UIKit控制器,UIKit中对实例属性的修改会同步到SwiftUI视图,实现双向状态同步。

备选方案:闭包回调

如果不需要全局共享状态,也可以用闭包的方式在UIKit中触发SwiftUI的状态更新:

  1. 在MyUIView中定义闭包属性:
struct MyUIView: UIViewControllerRepresentable {
    let onOptionChanged: () -> Void
    
    func makeUIViewController(context: Context) -> MyUIViewController {
        let vc = MyUIViewController()
        vc.onOptionChanged = onOptionChanged
        return vc
    }
    
    // ... 其他代码
}
  1. 在MyUIViewController中添加闭包属性:
class MyUIViewController: UIViewController {
    var onOptionChanged: (() -> Void)?
    // ...
    btn.addAction(UIAction(handler: { [weak self] _ in
        self?.onOptionChanged?()
    }), for: .touchUpInside)
}
  1. 在ContentView中使用:
MyUIView(onOptionChanged: {
    options.option = 0
})

但这种方案更适合局部状态传递,全局状态管理还是推荐共享ObservableObject的方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 03:59:51