Yes, one would expect that a window would notify its delegate or its controller with a windowWillAppear
or windowDidAppear
message, or post a documented notification like NSWindowDidAppearNotification
. But alas, none of those exist. I filed a bug report with Apple and was given the advice to use a storyboard and a view controller instead. This is unhelpful in legacy apps that already use a bunch of window controllers and xibs.
You could subclass NSWindow
and override orderWindow:relativeTo:
to send a notification. Most, but not quite all, of the messages that make a window show itself ultimately go through this method, including orderBack:
, orderFront:
, makeKeyAndOrderFront:
, and -[NSWindowController showWindow:]
. But orderFrontRegardless
does not go through orderWindow:relativeTo:
, so you would also want to override that for completeness.
Another way to be notified is to make a subclass of NSViewController
that controls some view that's always visible in the window. The view controller will receive viewWillAppear
and viewDidAppear
.
If you're subclassing NSWindow
or NSViewController
already for some other reason, either of these is a reasonable solution.
If you're not subclassing NSWindow
already, and don't have an NSViewController
subclass for a view that's always visible in the window, then another way is to use Cocoa bindings to connect the window's visible
binding to a property one of your objects. For example, I have a custom NSWindowController
subclass. I gave it a windowIsVisible
property:
@interface MyWindowController ()
@property (nonatomic) BOOL windowIsVisible;
@end
and I implemented the accessors like this:
- (BOOL)windowIsVisible { return self.window.visible; }
- (void)setWindowIsVisible:(BOOL)windowIsVisible {
NSLog(@"window %@ became %s", self.window, windowIsVisible ? "visible" : "hidden");
}
and in awakeFromNib
, I bind the window's visible
binding to the property like this:
- (void)awakeFromNib {
[super awakeFromNib];
[self.window bind:NSVisibleBinding toObject:self withKeyPath:NSStringFromSelector(@selector(windowIsVisible)) options:nil];
}
When the window becomes visible, the setWindowIsVisible:
setter is called with an argument of YES
. Note that if the whole app is hidden and reappears, the setter is called again, even though it wasn't called with argument NO
when the app was hidden. So be careful not to assume the window was previously hidden.
Also, the binding might create a retain cycle, so you should probably unbind it when the window is closed, unless you want to keep the window and controller around. Note that the window does post NSWindowWillCloseNotification
when it's closing, so you don't need any special magic to detect that.