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

ObservableObject中@Published更新后SwiftUI View未刷新问题排查

问题:嵌入UIKit的SwiftUI视图未响应ObservableObject更新

我知道已有很多类似问题,但大多是引用/值类型的问题,我的情况不一样。我通过UIHostingController在UIViewController中嵌入了SwiftUI视图,使用了ObservableObject类型的ViewModel,这个ViewModel在ViewController中实例化后传递给SwiftUI视图。但更新ObservableObject中的@Published属性时,SwiftUI视图并未同步刷新,请问哪里出错了?


ViewController代码

class DeveloperScreenViewController1: UIViewController {
let developerScreenViewModel: DeveloperScreenViewModel

private lazy var contentView: UIHostingController = {
    UIHostingController(rootView: DeveloperScreenView(developerScreenViewModel: developerScreenViewModel))
}()

init(viewModel: DeveloperScreenViewModel) {
    self.developerScreenViewModel = viewModel
    super.init(nibName: nil, bundle: Bundle.main)
}


override func viewDidLoad() {
    super.viewDidLoad()
     setupConstraints()

}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    self.developerScreenViewModel.setup()
}

private func setupConstraints() {
    // add swiftUI view as a subview
    addChild(self.contentView)
    self.contentView.view.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(self.contentView.view)
    
    // set constraints to match the parent viewcontroller's view
    NSLayoutConstraint.activate([
        view.leadingAnchor.constraint(equalTo: contentView.view.leadingAnchor),
        view.trailingAnchor.constraint(equalTo: contentView.view.trailingAnchor),
        view.topAnchor.constraint(equalTo: contentView.view.topAnchor),
        view.bottomAnchor.constraint(equalTo: contentView.view.bottomAnchor)
    ])
}

ViewModel代码

class DeveloperScreenViewModel: ObservableObject {

 @Published var environments: [Endpoint] = []
 @Published var realTimeUpdateToggle:Bool = false


public init() {
    // self.setup()
}

public func setup() {
    self.initializeListItems()
    self.initializeRealTimeToggle()
}

private func initializeListItems() {
    self.environments = EnvironmentStore.shared.changeableEndpoints
}

func initializeRealTimeToggle() {
   realTimeUpdateToggle = RealTimeUpdatesDeveloperUtils.isEnabled()
}

SwiftUI View代码

struct DeveloperScreenView: View {

@ObservedObject var developerScreenViewModel: DeveloperScreenViewModel


var body: some View { 
            VStack {
                
                DeveloperScreenEnvironmentView(endPoints: developerScreenViewModel.environments)
                
                
                DeveloperScreenRealTimeUpdateToggleView(realTimeToggleItem: developerScreenViewModel.realTimeUpdateToggle)
                
            }            
}

问题原因及修复方案

核心问题1:子视图接收的是值拷贝而非绑定

你的DeveloperScreenEnvironmentView和DeveloperScreenRealTimeUpdateToggleView直接接收的是属性的值拷贝,没有和ViewModel建立响应式关联。当ViewModel的@Published属性更新时,子视图持有的是旧值的副本,自然无法感知变化。

核心问题2:布局约束方向写反

setupConstraints中的约束逻辑错误,将父视图的锚点绑定到子视图,会导致布局异常,甚至可能隐藏SwiftUI视图(虽不是刷新失效的直接原因,但会引发其他UI问题)。

具体修复步骤:

  1. 子视图改用Binding传递参数
    修改子视图的输入参数为Binding类型,让它们直接绑定到ViewModel的@Published属性:

    // 修改DeveloperScreenEnvironmentView
    struct DeveloperScreenEnvironmentView: View {
        @Binding var endPoints: [Endpoint]
        // 其余视图逻辑保持不变
    }
    
    // 修改DeveloperScreenRealTimeUpdateToggleView
    struct DeveloperScreenRealTimeUpdateToggleView: View {
        @Binding var realTimeToggleItem: Bool
        // 其余视图逻辑保持不变
    }
    
    // 父SwiftUI视图中传递绑定
    struct DeveloperScreenView: View {
        @ObservedObject var developerScreenViewModel: DeveloperScreenViewModel
    
        var body: some View { 
            VStack {
                DeveloperScreenEnvironmentView(endPoints: $developerScreenViewModel.environments)
                DeveloperScreenRealTimeUpdateToggleView(realTimeToggleItem: $developerScreenViewModel.realTimeUpdateToggle)
            }            
        }
    }
    
  2. 修正布局约束方向
    调换锚点绑定顺序,确保子视图对齐父视图的边缘:

    NSLayoutConstraint.activate([
        contentView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        contentView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        contentView.view.topAnchor.constraint(equalTo: view.topAnchor),
        contentView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
    ])
    
  3. 可选:确保更新在主线程执行
    如果setup方法或后续属性更新是在后台线程触发的,必须切换到主线程——@Published的通知必须在主线程发送,否则SwiftUI无法正确刷新:

    public func setup() {
        DispatchQueue.main.async {
            self.initializeListItems()
            self.initializeRealTimeToggle()
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:41:08