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

SwiftUI:如何监听单例对象中字段的变化?

SwiftUI单例数据更新视图不刷新问题修复 + 类似React Context的替代方案

一、视图不更新的原因及解决办法

核心原因

SharedData中的@Published var sharedState = SharedState(),由于SharedState是值类型(结构体),直接修改它的内部属性(比如typeTrees.append(ttMaker))并不会触发@Published的变更通知——因为sharedState本身的实例没有被重新赋值,SwiftUI无法感知到内部数据的变化。

两种修复方案

方案1:修改后重新赋值sharedState

每次修改SharedState内部属性时,先创建副本修改,再重新赋值给sharedState,强制触发@Published的通知:

// 替换原有的直接append代码
var updatedState = SharedData.shared.sharedState
updatedState.typeTrees.append(ttMaker)
SharedData.shared.sharedState = updatedState

方案2:将SharedState改为ObservableObject类

把结构体改成遵循ObservableObject的类,并给需要监听的内部属性加上@Published,这样内部属性的变化会自动发送通知:

// 修改SharedState为类
class SharedState: ObservableObject {
    @Published var allMMS: [MMS] = []
    @Published var typeTrees: [TTMaker] = []
    @Published var sampleInputs: [String: String] = [:]
    @Published var selectedTypeTreeName: String?
    @Published var selectedMMSPathName: String?
    @Published var maps: [String: FunctionalMap] = [:]
    @Published var mapId: String?
    @Published var selectedMenuItem: String? = nil
}

// SharedData保持原有结构
class SharedData: ObservableObject {
    static let shared = SharedData()
    @Published var sharedState = SharedState()
}

同时,视图中建议用@StateObject(iOS14+)替代@ObservedObject来持有单例,确保生命周期正确:

struct ContentView: View {
    @StateObject var sharedData = SharedData.shared
    
    var body: some View {
        List {
            ForEach(sharedData.sharedState.typeTrees, id: \.self) { tree in
                Button(action: {
                    sharedData.sharedState.selectedTypeTreeName = tree.newTree.filename
                }) {
                    HStack {
                        Text(tree.newTree.filename)
                        Spacer()
                        if tree.newTree.filename == sharedData.sharedState.selectedTypeTreeName {
                            Image(systemName: "checkmark")
                        }
                    }
                }
            }
        }
    }
}

二、类似React Context的解决方案

SwiftUI中对应的是EnvironmentObject,可以在整个视图层级中共享状态,无需手动传递,和React Context的作用完全一致。

使用步骤

  1. 在App入口注入环境对象:
@main
struct YourApp: App {
    @StateObject private var sharedData = SharedData.shared
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(sharedData)
        }
    }
}
  1. 在任意子视图中通过@EnvironmentObject获取共享数据:
struct ContentView: View {
    @EnvironmentObject var sharedData: SharedData // 无需手动初始化,直接从环境获取
    
    var body: some View {
        List {
            ForEach(sharedData.sharedState.typeTrees, id: \.self) { tree in
                Button(action: {
                    sharedData.sharedState.selectedTypeTreeName = tree.newTree.filename
                }) {
                    HStack {
                        Text(tree.newTree.filename)
                        Spacer()
                        if tree.newTree.filename == sharedData.sharedState.selectedTypeTreeName {
                            Image(systemName: "checkmark")
                        }
                    }
                }
            }
        }
    }
}

这样无论视图层级多深,只要在环境中注入了sharedData,所有子视图都能直接访问,和React中useContext的用法逻辑一致。

内容的提问来源于stack exchange,提问作者János

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 06:09:19