'Button to open view in new window SwiftUI 5.3 for Mac OS X

I would like to have a button to open a new window and load a view (for the app's Preferences) in SwiftUI for MacOS but am unsure of the correct way to do it.

I tried creating a function and calling it from the button action which works but on closing the new window the app crashes with:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)

This is my function:

func openPreferencesWindow() {
    var preferencesWindow: NSWindow!
    let preferencesView = PreferencesView()
    // Create the preferences window and set content
    preferencesWindow = NSWindow(
        contentRect: NSRect(x: 20, y: 20, width: 480, height: 300),
        styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
        backing: .buffered,
        defer: false)
    preferencesWindow.center()
    preferencesWindow.setFrameAutosaveName("Preferences")
    preferencesWindow.contentView = NSHostingView(rootView: preferencesView)
    preferencesWindow.makeKeyAndOrderFront(nil)
}

And this is my button calling it:

Button(action: {
    openPreferencesWindow()
}) {
    Text("Preferences").font(.largeTitle).foregroundColor(.primary)
}

I feel like the window should be constructed in AppDelegate but I'm not sure how I would then call it.



Solution 1:[1]

You need to keep reference to created preferences window (like to the main window).

Here is possible solution (tested with Xcode 11.4 / macOS 10.15.5)

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {

    var window: NSWindow!
    var preferencesWindow: NSWindow!    // << here

    @objc func openPreferencesWindow() {
        if nil == preferencesWindow {      // create once !!
            let preferencesView = PreferencesView()
            // Create the preferences window and set content
            preferencesWindow = NSWindow(
                contentRect: NSRect(x: 20, y: 20, width: 480, height: 300),
                styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
                backing: .buffered,
                defer: false)
            preferencesWindow.center()
            preferencesWindow.setFrameAutosaveName("Preferences")
            preferencesWindow.isReleasedWhenClosed = false
            preferencesWindow.contentView = NSHostingView(rootView: preferencesView)
        }
        preferencesWindow.makeKeyAndOrderFront(nil)
    }

    // ... other code

and now button would look like

Button(action: {
    NSApp.sendAction(#selector(AppDelegate.openPreferencesWindow), to: nil, from:nil)
}) {
    Text("Preferences").font(.largeTitle).foregroundColor(.primary)
}

backup

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1