Scott
Scott

Reputation: 1094

How do you restrict the macOS windowing management from restoring a specific window?

I have an app that has a few windows defined as a windows group in the structure conforming to App in the main scene:

WindowGroup("StandingsView") {
    
    StandingsView()
        .environmentObject(appServices)
}
.handlesExternalEvents(matching: Set(arrayLiteral: "StandingsView"))

The appServices take some time to be configured, so I do not want to automatically restore the windows at start. I create the windows upon user selections being valid, the services being fully configured, and the user pressing a 'start' SwiftUI button:

if let standingsURL = URL(string: "raceStratLiteApp://StandingsView") {
    NSWorkspace.shared.open(standingsURL)
}

I've tried closing the windows in the appDelegate's applicationShouldTerminate(). I've also tried setting the isRestorable to false in applicationShouldTerminate:

func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
    
    for window in NSApplication.shared.windows {
        window.isRestorable = false
    }
    return .terminateNow
}

Are there any other methods to not restore a window? or better yet, to be able to programmatically restore it with its previous size, etc but launch only on user direction to 'start'

TIA

Upvotes: 7

Views: 918

Answers (4)

Lloyd Sargent
Lloyd Sargent

Reputation: 614

I use the following on a per window basis. It’s a view modifier so you can restrict it to a particular view instead of applying it to all views.

View Modifier

struct WillRestore: ViewModifier {
    let restore: Bool

    func body(content: Content) -> some View {
    content
        .onReceive(NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification), perform: { output in
            let window = output.object as! NSWindow
            window.isRestorable = restore
        })
    }
}

extension View {
    func willRestore(_ restoreState: Bool = true) -> some View {
        modifier(WillRestore(restore: restoreState))
    }
}

Usage

struct AboutRiverMakerContent: View {
    var body: some View {
        HStack(alignment: .center) {
            Text("Your Text Here")
        }
            .willRestore(false)
    }
}

If you want other windows to restore, you .willRestore or set the optional parameter for.willRestore to true, the window will restore. I have tested this and it seems to work with multiple windows (have not tested with WindowGroup).

I use it for the About MyApp to prevent it from restoring.

Caveats

  • This doesn’t seem to affect the main window. Since it does what I need, I haven’t pursued the issue in depth.
  • Positions WILL be saved
  • Size WILL be saved
  • Window menu WILL show the non-restore window. Hack is to remove the Window menu item

Edit: Added Window menu will show windows as marked non-restore. Minor code change.

Upvotes: 1

Vicente Garcia
Vicente Garcia

Reputation: 6390

The answer given by @Asperi was useful for most cases. But when the app gets interrupted, for example when it crashes, NSQuitAlwaysKeepsWindows seemed to be ignored.

What worked for me is to opt out every NSWindow from state restoration. The draw back is that this will affect every window at all. But no other solution worked on my case.

I had to use the NotificationCenter for this, listening to NSWindow. didBecomeKeyNotification, couldn't find a better alternative.

Example

On your NSApplicationDelegateAdaptor

func applicationWillFinishLaunching(_: Notification) {
    
    NotificationCenter
        .default
        .addObserver(forName: NSWindow.didBecomeKeyNotification,
                     object: nil,
                     queue: .main)
    { notification in
        guard let window = notification.object as? NSWindow else { return }
        window.isRestorable = false
    }
}

Drawbacks

  • This will affect ALL windows
  • No state restoration will work for windows, that includes size and position.

Upvotes: 0

Scott
Scott

Reputation: 1094

The code solution as @Asperi suggests in the later comment:

func applicationDidFinishLaunching(_ notification: Notification) {
    UserDefaults.standard.register(defaults: ["NSQuitAlwaysKeepsWindows" : false])
}

Upvotes: 5

Scott
Scott

Reputation: 1094

From the tip provide by @Asperi, writing the following to defaults will cease the writing of the window states:

$ defaults write <bundleid> NSQuitAlwaysKeepsWindows -bool false

So this is not a code change to the app, but rather an environment config the would be done on install.

I also deleted the savedState directory located at ~/Library/Saved Application State/<bundleid>.savedState for archive builds and at ~/Library/Containers/<App Name>/Data/Library/Saved Application State/<bundleid>.savedState for debug builds. Am not sure if that mattered but once doing these steps it solved the problem. Thanks @Asperi

Upvotes: 0

Related Questions