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?

Craig Otis
  • 31,257
  • 32
  • 136
  • 234

3 Answers3

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.

Craig Otis
  • 31,257
  • 32
  • 136
  • 234
  • 3
    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 Craig – Chris Birch Mar 12 '15 at 00:45
  • 4
    Actually you can simply let the autosave name empty in the Storyboard and just set it in code, it'll work ;) – Frizlab Apr 09 '16 at 21:07
  • @Frizlab 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.` – Craig Otis Apr 09 '16 at 21:19
  • 2
    @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`…). – Frizlab Apr 10 '16 at 22:59
  • @Frizlab 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! – Craig Otis Apr 11 '16 at 00:29
  • 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. – matt Apr 13 '17 at 17:43
  • @matt 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? – Craig Otis Apr 13 '17 at 17:44
  • 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. – matt Apr 13 '17 at 17:50
  • 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. – Patru Jun 14 '17 at 10:22
  • 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 ? – Klajd Deda Sep 05 '18 at 20:12
  • @Frizlab, thanks for your comments. It works and makes sense now - the subtle difference between near identical methods from NSWindow and NSWindowController. – Jerry Feb 16 '20 at 08:04
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"
    }
}
Atika
  • 1,560
  • 18
  • 18
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"
            }
        }
    }
}
DDP
  • 2,463
  • 1
  • 26
  • 28