SwiftUI DocumentGroup 并切换到后台

问题描述

我为“基于文档的应用程序”制作了一个基于新的 SwiftUI 多平台目标的应用程序。 但是,我面临着奇怪的问题。只要应用程序在前台,它就可以正常工作。如果通过任务切换将其移至后台,然后再次移至前台,则更改将保存到文档中,但 SwiftUI 视图不会接收更改。因此,每当您按下 UI 中的按钮来更改文档时,您将看不到任何事情发生,而一旦您从磁盘重新加载文档,更改就会出现。

所以我在想,我使用 Observedobjects,一旦我移到后台,它们可能会被踢出内存。这可能是我的错误的原因吗?

但后来我在 App 结构中添加了一行打印。

import SwiftUI

@main
struct MyApp: App {

    fileprivate func myLogging(_ file: FileDocumentConfiguration<MyDocument>) -> some View {
        print("""
                    IT IS CALLED
            """)

        return MainView().environmentObject(BindingWrapper(file.$document))
    }

    var body: some Scene {
        DocumentGroup(newDocument: MyDocument()) { (file) in
            return myLogging(file)
        }.commands { AppCommands() }
    }
}

猜猜看……这个打印总是在渲染突变之前执行。这是有道理的。因为 file.$document一个绑定,如果你做了一个改变操作,这个绑定会警告 Apple 文件是脏的,但它也会使整个层次结构无效。一旦发生错误,此日志仍将打印!

所以在 MainView().environmentObject(BindingWrapper(file.$document)) 行上,我假设一切都是从头开始创建的。 BindingWrapper 是我用来转换可观察对象中的绑定的自定义类。这是我担心的对象之一,他们可能会被释放。但是如果它们是新创建的……它们应该一直存在,对吗? 顺便说一下,这个对象归环境所有。所以它不应该被释放。

所以,现在我被卡住了。 Apple 是否对绑定 / Observedobjects 进行了一些巧妙的缓存,即使我认为一切都是新创建的,它也会将旧对象注入我的视图层次结构?

解决方法

尝试将任何连线/实例化移动到文档组的第一个视图。如果该视图包含您希望共享文档窗口生命周期的 StateObject,它们将不会被重建。

在下面的示例中,WindowStore 作为@StateObject 存放,如所述。位于 App 中的 RootStore 创建 WindowStore,其中包括自动售货服务并将其注册到受管理的窗口数组中。两者都可以启用您的日志记录服务。 (对我来说,当 @FocusedValue 失败时,该数组可帮助 WindowGroups 对特定文档进行操作(即,最顶部的文档不再是关键窗口)。

@main
struct ReferenceFileDoc: App {
    
    @StateObject var root: RootStore
    
    var body: some Scene {
        DocumentGroup { ProjectDocument() } editor: { doc in
            DocumentGroupRoot(
                window: root.makeWindowStore(doc.document),factory: SwiftUIFactory(root,doc.document)
            )
            .environmentObject(doc.document)
            .environment(\.documentURL,doc.fileURL)
            .injectStores(from: root)
        }.commands { Menus(root: root) }

    .... other scenes ...
struct DocumentGroupRoot: View {
    
    @EnvironmentObject var doc: ProjectDocument
    @Environment(\.undoManager) var undoManager
    @Environment(\.documentURL) var url

    @StateObject var window: WindowStore
    @StateObject var factory: UIFactory
    
    var body: some View {
        passUndoManagerToDocument()
        factory.reference(window)
        return DocumentWindow(vm: factory.makeThisVM()) // Actual visible window
            .focusedValue(\.keyWindow,window)
            .focusedValue(\.keyDocument,doc)
            .onAppear { /// Tasks }
            .reportHostingNSWindow { [weak window] in
                window?.setWindow($0)
            }
            .onChange(of: url) { [weak window] in window?.setFileURL($0) }
            .environmentObject(/// sub-state stores from WindowStore)
            .environmentObject(window)
            .environmentObject(factory)
    }
}