I ended up solving it in this way:
I made my AppDelegate conform to NSWindowDelegate so I could have access to some of the window lifecycle methods.
I saved the window position and size to UserDefaults whenever windowWillClose
triggers.
Originally, I was trying to use windowDidBecomeVisible
as the proxy for when the window was being open again (after being closed), but it wasn't triggering.
Instead, I had to use windowDidBecomeKey
. So every time windowDidBecomeKey runs, I grab the sizes and positions from UserDefaults and use window.setFrame to set the position.
Because windowDidBecomeKey also runs whenever the window gets unfocused and refocused (e.g. during cmd-tab), I had to create a flag for windowWasClosed
to only trigger my size updates when windowDidBecomeKey represents a new window opening and not just it going back into focus.
For me, this led to the window size changing to be instant for the user. Because windowDidBecomeKey
also runs on first launch, it will also restore sizing and position from a cold start.
Here's the whole code snippet in case others in the future find it useful:
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
var window: NSWindow!
var windowWasClosed = false
func applicationWillFinishLaunching(_ notification: Notification) {
NotificationCenter.default.addObserver(self, selector: #selector(windowDidBecomeKey(_:)), name: NSWindow.didBecomeKeyNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(windowWillClose(_:)), name: NSWindow.willCloseNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(windowDidResizeOrMove(_:)), name: NSWindow.didMoveNotification, object: window)
NotificationCenter.default.addObserver(self, selector: #selector(windowDidResizeOrMove(_:)), name: NSWindow.didResizeNotification, object: window)
}
@objc func windowDidBecomeKey(_ notification: Notification) {
if let window = notification.object as? NSWindow {
if windowWasClosed {
let windowOriginX = UserDefaults.standard.double(forKey: "windowOriginX")
let windowOriginY = UserDefaults.standard.double(forKey: "windowOriginY")
let windowWidth = UserDefaults.standard.double(forKey: "windowWidth")
let windowHeight = UserDefaults.standard.double(forKey: "windowHeight")
var frame = window.frame
frame.origin.x = windowOriginX
frame.origin.y = windowOriginY
frame.size.width = windowWidth
frame.size.height = windowHeight
window.setFrame(frame, display: true)
windowWasClosed = false
}
}
}
@objc func windowDidResizeOrMove(_ notification: Notification) {
if let window = notification.object as? NSWindow {
saveWindowPositionAndSize(window)
}
}
@objc func windowWillClose(_ notification: Notification) {
if let window = notification.object as? NSWindow {
windowWasClosed = true
saveWindowPositionAndSize(window)
}
}
func saveWindowPositionAndSize(_ window: NSWindow) {
let windowFrame = window.frame
UserDefaults.standard.set(windowFrame.origin.x, forKey: "windowOriginX")
UserDefaults.standard.set(windowFrame.origin.y, forKey: "windowOriginY")
UserDefaults.standard.set(windowFrame.size.width, forKey: "windowWidth")
UserDefaults.standard.set(windowFrame.size.height, forKey: "windowHeight")
}
}
ContentView
? Or provide minimal reproducible example? – Worshipful