How do you set an OSX app's window size and detect when it changes?

How the **** do you do this? I've tried changing the viewController's view size in the storyboard. That didn't work. Then I tried manually setting the view's frame and/or bound size in ViewDidLoad. That worked once, but then after I manually resize the view, it always comes up at that modified size. Not only that, but if I print out the view size in ViewWillAppear, it shows the values that I am trying to set it to. But the window that does appear is the size that I manually set it to earlier.???


And how do you detect when the window changes size? My viewWillTransition(to newSize: NSSize) function is never called. And I also set my application's delegate and overrode windowWillResize(_ sender: NSWindow, to frameSize: NSSize) -> NSSize. That also is never called.

So how do you do it?


Thanks

Is your window set to restore its state in the xib? You may be setting its frame before state restoration finishes. If you have "Restorable" checked in the xib AppKit will try to handle state restoration for you automatically (it is checked off by default).

windowDidResize should be called when window resized.

In a non document based App, I had issues with window resize and reposition: I wanted the window to be displayed with size and position saved after last close.

Finally, I had to use a patch to solve this.

I created a class property

var firstAppearance = true

In windowDidBecomeMain, I tested firstAppearance to resize

    func windowDidBecomeMain(notification: NSNotification) {
      
        if firstAppearance {
            if let window = window, screen = window.screen {
                var frame : NSRect 
               //  compute the new frame
                self.window!.setFrame(frame, display: true)
          }
            firstAppearance = false
     }

In windowWillClose,windowDidMove, windowDidResize, I save the position and size.

Why do you override windowWillResize(_ sender: NSWindow, to frameSize: NSSize)

I think you need only to implement, not override.

Actually I was just implementing, not overriding. And the reason it did not call windowWillResize is that I was using NSApplication.shared.mainWindow to get the window in my viewDidLoad function, and that was nil.


Instead I subclassed NSWindowController and set the delegate there, and now windowWillResize is called.


I'm still not able to control the window size/position. I set up a function like you list above, but the window still always appears the way I let set it manually.

>And I also set my application's delegate and overrode windowWillResize(_ sender: NSWindow, to frameSize: NSSize) -> NSSize. That also is never called.


You set the application's delegate? Did you set the window's delegate? There are several delegate methods related to size changes declared in NSWindow.h (under NSWindowDelegate) including:



- (void)windowDidResize:(NSNotification *)notification;
- (void)windowWillStartLiveResize:(NSNotification *)notification NS_AVAILABLE_MAC(10_6);
- (void)windowDidEndLiveResize:(NSNotification *)notification NS_AVAILABLE_MAC(10_6);


According to the appkit release notes, you may want to register for the notifications (not set NSWindowDelegate) if you are using NSWindowCOntroller in conjunction with your window, since they are putting things on lock down.


With that said, I ususally don't use the window delegate methods to do things (other than maybe fade out on willStartLiveResize, and fade in after windowDidEndLiveResize).


Otherwise, when I do manual layout, since there is no viewDidLayoutSubviews method on macOS, I just listen for the window's content view frame change:


-(void)viewDidLoad
{
  [super viewDidLoad];
  //assume this view controller is the window's contentViewController.
   self.view.postsFrameChangedNotifications = YES;
    [[NSNotificationCenter defaultCenter]addObserver:self
                                            selector:@selector(viewFrameDidChange:)
                                                name:NSViewFrameDidChangeNotification
                                              object:self.view];

}

-(void)viewFrameDidChange:(NSNotification*)note
{
   NSLog(@"frame changed");
}

-(void)dealloc
{
[[NSNotificationCenter defaultCenter]removeObserver:self
                                      
                                                name:NSViewFrameDidChangeNotification
                                              object:self.view];
}


I know there's viewDidLayout or something on a view controller on macOS, but I recall running into some issues making changes in this method, so I just avoid it.

Ah, okay thanks that helps! I was getting calls to windowDidResize, but those are actually a different size than the view's size, I guess because of the title bar. At least now my rendering is working.


But I still have zero control over the window's initial size and position. It seems to have a mind of its own. Sometimes it shows up where I last left it, sometimes it shows up with some sort of default. If I try to set it, the printed out values appear correct but it has no effect on the window itself. It shows up where and at the size it wants.


How do you do it? I've just been trying window.setFrame(). I've tried it in windowDidLoad(), windowDidBecomeMain(), and viewDidLoad(), and nothing works!

Thanks

Accepted Answer

Is your window set to restore its state in the xib? You may be setting its frame before state restoration finishes. If you have "Restorable" checked in the xib AppKit will try to handle state restoration for you automatically (it is checked off by default).

Yes! I had 'Restorable' checked. Once I unchecked it then things started working.


How does that 'Restorable' work? When I did have it checked, it still acted unpredictably regarding size/placement. Sometimes it came back up where I'd left it but sometimes it seemed to come up in its default position. Are you supposed to do something to 'save' the position for the restorable to work?

>How does that 'Restorable' work? When I did have it checked, it still acted unpredictably regarding size/placement. Sometimes it came back up where I'd left it but sometimes it seemed to come up in its default position. Are you supposed to do something to 'save' the position for the restorable to work?


It can seem to act unpredictably when you are running from the debugger because AppKit saves state information I believe in NSUserDefaults, so if you just hit "Stop" in Xcode, user defaults may or may not have been synchronized.


Depending on what you are trying to achieve, you may wish to check out these two NSApplicationDelegate methods:


Application Restorable State

application:didDecodeRestorableState:

Tells the delegate the application has extracted its restorable state from a given archiver.



application:willEncodeRestorableState:

Tells the delegate the application will encode any application state that was preserved between application launches.


How do you set an OSX app's window size and detect when it changes?
 
 
Q