ObservableObject 和存储 NSWindow 对象的@State有什么区别

What is the difference between ObservableObject and @State to store a NSWindow object

提问人:Trufa 提问时间:11/8/2023 更新时间:11/8/2023 访问量:87

问:

我正在动态/编程地创建这样的窗口:

func createWelcomeWindow() {
    let w = WelcomeScreenView().frame(width: 400, height: 600)
    let hostingController = NSHostingController(rootView: w)
    appState.welcomeWindow = NSWindow(contentViewController: hostingController)
    appState.welcomeWindow?.title = "Welcome to Last Warning"
    appState.welcomeWindow?.collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary]
    appState.welcomeWindow?.makeKeyAndOrderFront(nil)
}

appState 是这样创建的:

class AppState: ObservableObject {
    @Published var warningWindow: NSWindow?
    @Published var welcomeWindow: NSWindow?
}

@main
struct LastWarningApp: App {
    @ObservedObject var appState = AppState()

我对 swift 中的状态管理感到困惑。

我试过了,主要是出于好奇,换了

@main
    struct LastWarningApp: App {
        @State var welcomeWindow: NSWindow?

然后做:

welcomeWindow = NSWindow(contentViewController: hostingController)
welcomeWindow?.title = "Welcome to Last Warning"
welcomeWindow?.collectionBehavior = [.moveToActiveSpace, .fullScreenAuxiliary]
welcomeWindow?.makeKeyAndOrderFront(nil)

但这并没有创建窗口,我想知道为什么会这样,据我所知,问 chatGPT,@State似乎用于管理视图的状态,不适合这个,我已经浏览了尽可能多的 github 存储库,试图查看最佳实践,但我找不到明确的方法来解决这个问题。

我注意到了一件事,但我不确定,那就是当我调试它时,似乎没有设置欢迎窗口。

那么,这是一个合理的方法吗?为什么@State在这种特殊情况下不起作用?

这也让我感到困惑,我可以做:

@State var appState = AppState()

行为保持不变。

Swift Xcode macOS SwiftUI

评论

1赞 Sweeper 11/8/2023
你为什么要这样做?这听起来像是一个巨大的 XY 问题。为什么首先需要存储 s?使用纯 SwiftUI 完成所有这些操作怎么样?NSWindow
0赞 Trufa 11/8/2023
@Sweeper我很乐意用纯 swiftui 来做到这一点,但我还没有找到任何方法,如果我创建一个 windowGroup,这似乎会在启动时打开一个窗口,这是一个菜单栏应用程序,仅在某些情况下打开一个窗口,我很乐意切换到任何纯 swiftui 纯方法, 即便如此,我认为这是一个很好的学习机会。
2赞 Sweeper 11/8/2023
请展示一个最小的可重现示例,我可以将其复制并粘贴到新项目中并重现您的行为。现在你只显示不连贯的代码片段,不清楚你把它们放在哪里。 除其他外,甚至没有被召唤。createWelcomeWindow
3赞 Sweeper 11/8/2023
“这是一个菜单栏应用程序,仅在某些情况下打开一个窗口”好的,太好了。这就是您要解决的实际问题。而是问这个问题。您希望在什么情况下显示窗口?窗口显示什么(这可能有助于决定是否使用或)?WindowGroupWindow
0赞 Trufa 11/8/2023
@Sweeper我认为这是两个不同的问题,这段代码按预期工作,我想了解为什么另一个代码不能更好地理解编程语言,老实说,也许我应该删除最佳实践部分作为一个单独的问题

答:

1赞 lorem ipsum 11/8/2023 #1

@State作为事实来源,并在“值”发生变化时重新绘制。body

没有任何问题

@State var appState = AppState()

您只需要知道,更改引用类型的属性不会触发重绘,因为该值不会更改。<<<<< 非常重要

ObservableObject需要一系列的东西才能工作。

@Published在值更改时发出,>合成“Object”属性包装器>发出以使 .ObservableObjectView

现在的“间隙”,不是 or 的引用类型,这些可以存储,但不会重绘,因为没有机制。ObservableObjectObervable@StateView

您可能需要在 SwiftUI 中存储这些内容,因为 SwiftUI 是一种值类型,可以随时由 SwiftUI 重新创建。如果您不使用它,您最终将获得多个实例。View

与“Object”属性包装器存在差异。 用于初始化,用于传递。StateObjectObservedObject

  @ObservedObject var appState = AppState()

不正确.....

你应该使用

   @StateObject var appState = AppState()

这背后的主要区别在于可以访问 SwiftUI 拥有的秘密存储。

在 iOS 13 中,初始化的“正确”方式是 ,但我们无法观察到它,直到我们可以使用的下一个视图。 是在 iOS 14 中引入的。ObservableObjectStateObservedObjectStateObject

现在这是 SwiftUI 属性包装器的基础知识,但我认为您最大的问题是您混合了 SwiftUI 和 AppKit,SwiftUI 有一种非常不同的窗口管理方式。

https://developer.apple.com/videos/play/wwdc2022/10061/

https://developer.apple.com/documentation/swiftui/bringing_multiple_windows_to_your_swiftui_app

在 .App

@main
struct BookClub: App {
    @StateObject private var store = ReadingListStore()

    var body: some Scene {
        WindowGroup {
            ReadingListViewer(store: store)
        }
        Window("Activity", id: "activity") {
            ReadingActivity(store: store)
        }
    }
}

你向他们展示类似的东西

struct OpenWindowButton: View {
    @Environment(\.openWindow) private var openWindow

    var body: some View {
        Button("Open Activity Window") {
            openWindow(id: "activity")
        }
    }
}