Reputation: 6070
I am building a single window application and want to use the new SwiftUI App Lifecycle.
import SwiftUI
@main
struct SingleWindowApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
The default implementation of WindowGroup
allows multiple instances of the window (i.e. if you hit ⌘N
). I wasn’t able to find a modifier that changes that behaviour.
How would I limit the number of windows within a WindowGroup to just 1?
Upvotes: 11
Views: 4048
Reputation: 7663
This should do it:
import SwiftUI
@main
struct SingleWindowApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
CommandGroup(replacing: .newItem, addition: { })
}
}
}
Upvotes: 9
Reputation: 483
In Xcode 14 it's as simple as creating a Scene
with only a Window
with id main
.
This opens the window and does not include a File -> New
menu item.
var body: some Scene {
Window("My Single Window", id: "main") {
ContentView()
}
}
Relevant documentation here: https://developer.apple.com/documentation/swiftui/window#Use-a-window-as-the-main-scene
Upvotes: 8
Reputation: 9454
Here's how you can use normal SwiftUI code to disable "File -> New Window" when the main window is open, and enable "File -> New Window" when the main window is closed.
This code probably has some edge cases that could be polished, but it does work.
import SwiftUI
@main
struct MyApp: App {
@State var isMainWindowOpen = false
var body: some Scene {
WindowGroup {
ContentView()
.onAppear {
print("Main window appeared")
self.isMainWindowOpen = true
}
.onDisappear {
print("Main window disappeared")
self.isMainWindowOpen = false
}
}.commands {
if isMainWindowOpen {
CommandGroup(replacing: .newItem) {
Button("New Window", action: {})
.disabled(true)
// This is the same keyboard shortcut as the default New Window option.
// We're just doing this so that our disabled dummy option shows
// the same shortcut visually.
.keyboardShortcut(KeyboardShortcut("n", modifiers: [.command]))
}
} else {
// By doing nothing here, we let the default
// "File -> New Window" item display and handle input.
EmptyCommands()
}
}
}
}
Upvotes: 1
Reputation: 113
Each time you open a WindowGroup object, you can use NSViewControllerRepresentable to get the NSWindow instance of the view.
Then consider defining an external object to store the collection of NSWindows. The next time you open the WindowGroup object, if the NSWindow collection is full, find a corresponding NSWindow to display. for example
if windowList.isfull {
let window = windowList.getWindow()
window.makeKey()
window.orderFront(nil)
} else {
NSWorkspace.shared.open(url)
}
On how to obtain an instance of NSWindow from WindowGroup, you can consider an open source implementation: https://github.com/happycodelucky/SwiftUIWindowBinder
Upvotes: 2
Reputation: 1630
As i was facing the same problem, but on iPad where the command modifier has no effect, I found this: There's an "Application Scene Manifest" UIApplicationSceneManifest
property in your Info.plist that is a dictionary and as a child "Enable Multiple Windows" UIApplicationSupportsMultipleScenes
which is set to YES
by default. Settings this option to NO
gives the desired effect :)
Upvotes: 3