I am writing a document-based app for macOS using SwiftUI. I want the File menu's New Document command to show a template picker/wizard, and then let the wizard create the document.
How do I structure this? Is there documentation? Examples?
I tried this pattern
@main struct DocDemoApp: App {
var body: some Scene {
WindowGroup { NewDocWizard() }
DocumentGroup(newDocument: { DocDemoDocument() }) {
ContentView(document: $0.document)
}
}
}
The NewDocWizard calls newDocument({ DocDemoDocument() })
. But the WindowGroup
makes a File > New Window command while DocumentGroup
makes the File > New Document command. I need just New Document and it should show the NewDocWizard
.
Answering my own question. I want to use Commands. See newItem.
Here's an example:
struct DocDemoApp: App {
@Environment(\.openWindow) var openWindow
@Environment(\.openDocument) private var openDocument
var body: some Scene {
DocumentGroup(newDocument: { DocDemoDocument() }) { config in
ContentView(document: config.document)
}
Window("Choose Template", id: "wizard") {
NewDocWizard()
}
.commands {
CommandGroup(replacing: .newItem) {
Section {
// Menu File >
Button("New") { openWindow(id: "wizard") }.keyboardShortcut(KeyboardShortcut("N"))
Button("Open") { open() }.keyboardShortcut(KeyboardShortcut("o"))
Button("Open Recent...") { print("NYI") }
}
}
CommandGroup(replacing: .singleWindowList) {
// Menu Window > Choose Template, ... (list of single windows)
// Hide and force user to use File > New for this window
}
}
}
private func open() {
let panel = NSOpenPanel()
panel.allowedContentTypes = [.exampleText]
panel.allowsMultipleSelection = true
panel.canChooseDirectories = false
panel.runModal()
if let url = panel.url {
Task {
try! await openDocument(at: url)
}
}
}
}