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

SwiftUI跨视图修改@Published变量同步更新的最优实现方案

你对问题的判断完全正确,不同视图持有独立的ExerciseSelector实例时,@Published的变更通知只会发送给当前实例的观察者,自然无法跨视图同步状态。

单例方案的弊端

单例并不是该场景的最优解,存在明显缺陷:

  • 强耦合:所有视图直接依赖全局实例,后续要做多训练计划并行、多账号切换等需求时完全无法扩展
  • 可测试性差:无法针对不同测试场景初始化不同状态的ExerciseSelector实例
  • 状态不可控:全局实例的状态可被任意位置修改,问题排查难度高

更合理的实现方案

SwiftUI官方推荐的跨视图共享可观察对象的方案有两种,根据你的视图层级选择即可:

方案1:环境对象传递(适合多层级、多视图共享)

在两个视图的共同父视图(一般是TabView容器或者App入口)持有唯一的ExerciseSelector实例,注入环境后所有子视图都可以直接读取:

// App入口示例
@main
struct WorkoutApp: App {
    // 用@StateObject持有实例,生命周期和App绑定
    @StateObject private var exerciseSelector = ExerciseSelector()
    
    var body: some Scene {
        WindowGroup {
            TabView {
                ContentView()
                    .environmentObject(exerciseSelector)
                    .tabItem { Text("训练") }
                NewRoutine()
                    .environmentObject(exerciseSelector)
                    .tabItem { Text("新建计划") }
            }
        }
    }
}

子视图直接从环境读取实例,不需要自行初始化:

struct ContentView: View {
    @EnvironmentObject var exercises: ExerciseSelector
}

struct NewRoutine: View {
    @EnvironmentObject var exerciseSelector: ExerciseSelector
    
    func printThings() {
        exerciseSelector.select(random: true)
    }
}

注意:使用@EnvironmentObject前必须确保父视图已经注入对应类型的实例,否则会触发运行时崩溃。

方案2:构造参数传递(适合层级简单的场景)

如果你的视图层级很浅,直接通过构造参数把实例传递给两个子视图即可:

// TabView容器父视图
struct TabRootView: View {
    @StateObject private var exerciseSelector = ExerciseSelector()
    
    var body: some View {
        TabView {
            ContentView(exercises: exerciseSelector)
                .tabItem { Text("训练") }
            NewRoutine(exerciseSelector: exerciseSelector)
                .tabItem { Text("新建计划") }
        }
    }
}

// 子视图声明接收参数
struct ContentView: View {
    @ObservedObject var exercises: ExerciseSelector
}

struct NewRoutine: View {
    @ObservedObject var exerciseSelector: ExerciseSelector
}

方案选型建议

以上两种方案的可维护性、可扩展性、可测试性都远高于单例,是这类跨视图共享状态场景的首选方案。只有当你的状态确实是全局唯一、整个App生命周期只会存在一个实例时,才考虑使用单例实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 11:15:07