macOS SwiftUI: Создание экземпляра нового окна без документов в приложении для документов

#swiftui #macos-big-sur

Вопрос:

Есть ли чисто быстрый способ открыть новое окно? У меня есть приложение на основе рабочего документа, приложение которого выглядит так:

 @main struct MyApp: App
{
    var
    body: some Scene {
        DocumentGroup(newDocument: ProjectDocument.init) { inGroup in
            ProjectWindowContentView(document: inGroup.document)
                .frame(minWidth: 301.0, minHeight: 100.0)
                .onReceive(self.addTerrainGeneratorLayerCommand) { _ in
                    inGroup.document.addTerrainGeneratorLayer()
                }
        }
        .commands {
            ...
        }
    }
    ...
}
 

Теперь я хочу добавить команду меню для создания небольшого автономного утилиты в собственном окне. До сих пор любое обсуждение, которое я вижу в Интернете, на самом деле включает в себя создание нового NSWindow представления с NSHostingView содержанием. Это кажется не очень быстрым, и, учитывая их недавние дополнения DocumentGroup и WindowGroup , похоже, довольно большую оплошность.

Я попытался вставить a WindowGroup в сцену приложения, но оно показывает мое окно только в том случае, если я закомментирую DocumentGroup .

Ответ №1:

Нам нужно WindowGroup окно для окна, поэтому первый/простой шаг-просто добавить его, например

(Демонстрационная версия, созданная с помощью Xcode 12.5 / macOS 11.3, на основе шаблона приложения для документов)

 struct PlayDocumentXApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: PlayDocumentXDocument()) { file in
            ContentView(document: file.$document)
        }
        WindowGroup("Tools") {
            Text("This is tool window")
                .frame(width: 200, height: 400)
        }
    }
}
 

и у нас это доступно через File > New меню

ДЕМОНСТРАЦИЯ

Если мы хотим создать/показать его программно из другого места, скажем, с помощью команд, мы можем использовать подход на основе URL, например

демо2

 struct PlayDocumentXApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: PlayDocumentXDocument()) { file in
            ContentView(document: file.$document)
        }
        .commands {
            CommandMenu("Test") {
                Button("Show Tools") {
                    NSWorkspace.shared.open(URL(string: "myTools://my")!)
                }
            }
        }
        WindowGroup("Tools") {
            Text("This is tool window")
                .frame(width: 200, height: 400)
                .handlesExternalEvents(preferring: Set(arrayLiteral: "myTools://my"), allowing: Set(arrayLiteral: "myTools://my"))
        }
    }
}
 

демонстрация3

Комментарии:

1. Я не понимал, что он вставляет для этого пункт меню в Файл->Создать. На данный момент этого достаточно. Описанный вами подход на основе URL-адресов, безусловно, кажется обходным путем, но я думаю, что он работает.

2. Кроме того, спасибо за отличный и обстоятельный ответ!

Ответ №2:

Из предыдущего отличного ответа я пришел к этому фрагменту.

  • В нем показано, как добавить команду в Window меню
  • Синтаксис немного короче ( handleExternalEvents(соответствие:) )
 WindowGroup("Tools", id: "tools") {
    Text("This is tool window")
}
.commands {
    CommandGroup(after: CommandGroupPlacement.windowList) {
        Button(action: {
            NSWorkspace.shared.open(URL(string: "myTools://my")!)
        }, label: {
            Text("Tool Window")
        })
    }
}
.handlesExternalEvents(matching: ["myTools://my"])