NSWindowController Autosave using Storyboard
Asked Answered
R

3

16

I have a Swift application that is launching a simple NSWindow, like so:

func applicationDidFinishLaunching(notification: NSNotification!) {
    let storyboard = NSStoryboard(name: "MainStoryboard", bundle: NSBundle.mainBundle())
    windowController = storyboard.instantiateInitialController() as? NSWindowController
    windowController?.showWindow(self)
}

This works great, my app launches and the window appears. However: The size is always the same.

In the storyboard, I have specified an Autosave name:

MainStoryboard

Note also the Restorable checkbox is checked.

But regardless, the window appears the same size, every time. This has always "just worked" in the past, so I can't tell if this is a bug, or a piece I'm missing. Does autosaving automatically work with storyboards when instantiating and showing windows manually?

Reconnaissance answered 6/8, 2014 at 0:0 Comment(0)
R
31

This seems to be an Xcode bug. I was able to workaround it by manually setting the NSWindowController windowFrameAutosaveName property:

windowController?.windowFrameAutosaveName = "Main App Window"

However... This only worked for me if the property was set to a different value than what is displayed in Interface Builder. If it's programmatically set to the same value that's being used in IB, it doesn't work.

So in IB the autosave name is left to MainAppWindow, and programmatically it's set to Main App Window.

Reconnaissance answered 12/8, 2014 at 0:46 Comment(11)
This doesn't seem to be just swift related. I had the same problem in ObjC, solved by adding self.windowFrameAutosaveName = @"MainWindow"; to awakeFromNib. Also I set the name to "WRONG" in IB storyboard. Did the trick. Thanks CraigScroop
Actually you can simply let the autosave name empty in the Storyboard and just set it in code, it'll work ;)Thedrick
@Thedrick Doesn't an empty autosave name suggest that you don't want frame information persisted? From the NSWindowController docs: By default, name is an empty string, which means that no information is stored in the defaults database.Reconnaissance
@CraigOtis of course. But in the storyboard, you change the autosave name of the NSWindow, not the one of the NSWindowController. The one from the NSWindowController takes over (it actually overwrite the autosave name of the NSWindow…).Thedrick
@Thedrick Ah, you're right - I didn't consciously realize the difference. I unfortunately don't have this particular Xcode project anymore, but I'd be interested to see how changing this property on the NSWindowController behaved!Reconnaissance
The weird part is that if I just make a plain vanilla new project, there's no issue. So why does my actual app project have this problem? And why (in my case) only for the main window? It's a mystery, and it means I can't submit a bug report to Apple because I can't reproduce the issue.Pottery
@Pottery Yea, I never really got to the bottom of this one. It was super inconsistent, and I haven't seen the issue since. Are you still seeing it in Xcode 8.3?Reconnaissance
I see it in my actual application's main window in Xcode 8.3, but not for a new vanilla project created in Xcode 8.3. But my actual application was a new vanilla project a week ago, so what's the difference between the two? It's a mystery. However, as advertised, making an NSWindowController subclass and implementing awakeFromNib and setting the frame autosave name there fixed the problem.Pottery
While playing around with this as I switched an old .nib to a .storyboard I noticed a really strange behaviour. I set the Autosave property on the window in IB first and it did not work. After finding this thread I moved this to code (I did it in viewDidAppear since I already had it). I used the same Identifier I had set before in IB and changed the IB-Autosave to something nonsensical. Strangely enough when I restarted the application the window would immediately appear at its saved location. So the location got saved, but something (probably the Seague?) "overrode" it.Cidevant
Thank you Craig for the solution. So whats the point of IB settings, if half the time i have to guess of what the heck is happening ?Labarum
@Frizlab, thanks for your comments. It works and makes sense now - the subtle difference between near identical methods from NSWindow and NSWindowController.Phillida
C
7

I don't know if there is a better way, but the problem here is that when the AutosaveName is set in Interface Builder, and the Window is open via a Segue, the window is open at a predefined location and the frame is saved, overwriting the last saved frame...

If you have a predefined position to the center of the screen, each time the window will be open, it will appear in the center and the position will be saved.

To avoid this, I set the AutosaveName in the Window Controller (Not in IB), after restoring the saved Frame:

class MyWindowController: NSWindowController {
    override func windowDidLoad() {
        super.windowDidLoad()

        let thewindow = window as! NSWindow

        /// restore position
        thewindow.setFrameUsingName("MyWindow")
        self.windowFrameAutosaveName = "MyWindow"
    }
}
Counterpart answered 23/5, 2017 at 16:15 Comment(3)
it worked for me, but only after I deleted the "Autosave Name" value from IB.Coincident
Yes it was the purpose of my answer "set the AutosaveName in the Window Controller (Not in IB)" ;)Counterpart
"not in IB" doesn't necessarily meant "delete it from IB" to meCoincident
P
5

In order to satisfy the two conditions identified in the accepted answer and comments, subclassing the window controller seems to work. You can then avoid setting this property uniquely in code for every window controller and simply specify the storyboard's window autosave property (and set the window controller's subclass).

The WindowController's windowFrameAutosaveName must be

  1. not blank
  2. different to Window's frameAutosaveName
class AutoFrameSavingWindowController: NSWindowController
{
    override func awakeFromNib() {
        if let autosaveName = window?.frameAutosaveName {
            windowFrameAutosaveName = autosaveName + " temp"
            }
        }
    }
}
Premonitory answered 29/4, 2020 at 21:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.