如何在同一SwiftUI macOS应用的多窗口间共享状态
Mac平台SwiftUI多窗口状态共享方案
问题描述
我希望在Mac平台的同一SwiftUI应用的多个窗口之间共享状态。根据文档说明,@State和@StateObject无法实现状态共享,我的示例代码也验证了这一点。请问该如何实现两个窗口间的状态共享?
示例代码:
@main struct TM_window_TestApp_2: App { @State var state: String? @State var selectedInSub: Int? @StateObject var stateObject = SimpleStateObject() var body: some Scene { WindowGroup { VStack{ SubView(selected: $selectedInSub) Text("\(selectedInSub == nil ? \"nothing selected\" : selectedInSub!.formatted())") Text("------------") Text("shared State: ??") Text(stateObject.s) Text("\(state == nil ? \"nil\" : state!)") Button("change StateObject"){stateObject.s.append("_x")} Button("change State"){if state == nil { state = "state"} else{ state?.append("_y") } } } } } } struct SubView: View { @Binding var selected: Int? @State var selectedLocal : Int? var values = [1,2,3,4,5,6,7] var body: some View { NavigationView{ List(values, id: \.self, selection: $selectedLocal) { val in Text("\(val)") } } } } class SimpleStateObject: ObservableObject { @Published var s = "stateObject" }
有趣的是,在iPad应用的分屏模式下,@State和@StateObject是可以实现状态共享的。
实现方案
在Mac SwiftUI中,多窗口无法共享@State/@StateObject的核心原因是:每个窗口都是WindowGroup创建的独立视图实例,会各自初始化自己的状态对象。要实现共享,需让状态脱离单个窗口的生命周期,成为应用级全局实例,以下是两种常用方案:
1. 使用@AppStorage(适合轻量持久化状态)
如果状态是字符串、数字这类基础类型,且需要持久化,@AppStorage直接绑定系统存储,天然支持多窗口同步:
@main struct TM_window_TestApp_2: App { @AppStorage("sharedState") var state: String? @AppStorage("selectedValue") var selectedInSub: Int? var body: some Scene { WindowGroup { VStack{ SubView(selected: $selectedInSub) Text("\(selectedInSub == nil ? "nothing selected" : selectedInSub!.formatted())") Text("------------") Text("shared State: ??") Text("\(state == nil ? "nil" : state!)") Button("change State"){ if state == nil { state = "state" } else { state?.append("_y") } } } } } }
2. 全局单例+@ObservedObject(适合复杂状态对象)
对于自定义的ObservableObject,通过全局单例或App级静态属性确保所有窗口共用同一个实例:
方式一:全局单例
class SimpleStateObject: ObservableObject { static let shared = SimpleStateObject() // 全局单例 @Published var s = "stateObject" } @main struct TM_window_TestApp_2: App { @State var state: String? @State var selectedInSub: Int? @ObservedObject var stateObject = SimpleStateObject.shared // 引用单例 var body: some Scene { WindowGroup { VStack{ SubView(selected: $selectedInSub) Text("\(selectedInSub == nil ? "nothing selected" : selectedInSub!.formatted())") Text("------------") Text("shared State: ??") Text(stateObject.s) Text("\(state == nil ? "nil" : state!)") Button("change StateObject"){stateObject.s.append("_x")} Button("change State"){ if state == nil { state = "state" } else { state?.append("_y") } } } } } }
方式二:App层级静态属性
class SimpleStateObject: ObservableObject { @Published var s = "stateObject" } @main struct TM_window_TestApp_2: App { static let stateObject = SimpleStateObject() // App级静态实例 @State var state: String? @State var selectedInSub: Int? @ObservedObject var sharedStateObject = TM_window_TestApp_2.stateObject var body: some Scene { WindowGroup { VStack{ SubView(selected: $selectedInSub) Text("\(selectedInSub == nil ? "nothing selected" : selectedInSub!.formatted())") Text("------------") Text("shared State: ??") Text(sharedStateObject.s) Text("\(state == nil ? "nil" : state!)") Button("change StateObject"){sharedStateObject.s.append("_x")} Button("change State"){ if state == nil { state = "state" } else { state?.append("_y") } } } } } }
补充说明:iPad分屏的特殊情况
iPad分屏模式下,多个窗口属于同一个视图层级的分支,共享App结构体的状态实例,因此@State/@StateObject可以直接共享;而Mac的多窗口是完全独立的视图实例,各自初始化状态,所以无法直接共享。
内容的提问来源于stack exchange,提问作者mica
相关产品推荐
相关产品推荐

