ixany
ixany

Reputation: 6070

How to limit the number of windows in a WindowGroup?

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

Answers (5)

Mihai Fratu
Mihai Fratu

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

kdubb
kdubb

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

danneu
danneu

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

user1397892
user1397892

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

warly
warly

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 :)

Info.plist for disabling multiple window support

Upvotes: 3

Related Questions