19

I'm developing an app on MacOS X with Xcode5.1

and there's an action I want to trigger everytime the user opens or shows a NSWindowController all I found was

  • windowDidLoad
  • windowWillLoad
  • awakeFromNib

but nothing like in iOS: my methods...

  • viewWillAppear
  • viewDidAppear

because even if I close an NSWindowController with

[NSWindowController close];

if I open it again, it doesn't trigger my actions from windowDidLoad, windowDidAppear or awakeFromNib

and now I need something like them, what's the equivalent, it must be something

thanks in advance for the support

Jesus
  • 8,456
  • 4
  • 28
  • 40

2 Answers2

31

Yes, there's no such convenient methods in NSWindowController. Let me explain why.
There is a difference between iOS view controller and OS X window controller: in iOS, view controller can appear fullscreen or completely hide from screen. That's all. The window in OS X has many more degrees of freedom: it can be shown, hidden, resized, minimized/restored, overlayed by other apps' windows, go fullscreen, go to another screen (in multimonitor configuration), etc. For tracking all this activity, NSWindow has a delegate (which is automatically mapped on corresponding NSWindowController in xib). Take a look at NSWindowDelegate docs. So there's no direct behavioral mapping between iOS "appear" and OS X bunch of actions. But we can try to use the nearest possible events.

For your case (do something at window become visible), I can offer 2 different methods.
First, override the showWindow action in your NSWindowController subclass:

- (IBAction)showWindow:(id)sender
{
    [super showWindow:sender];

    // your code here
}

This way, your code will be called every time window is created/shown on screen.

Or second, use the delegate method:

- (void)windowDidChangeOcclusionState:(NSNotification *)notification
{
    // notification.object is the window that changed its state.
    // It's safe to use self.window instead if you don't assign one delegate to many windows
    NSWindow *window = notification.object;

    // check occlusion binary flag
    if (window.occlusionState & NSWindowOcclusionStateVisible)  
    {
        // your code here
    }
}

This way, your code will be called every time when window (or it's part) will become visible. For example, this event can occur if user minimized the other window that was over your window (or moved it somewhere). It is usual when you want to suspend animation/timers/etc in invisible window to save some cpu :)
It's also very useful method if you need to do something on window disappear (for example, windows with enabled hidesOnDeactivate flag is not closed and does not call corresponding delegate methods; they just removed from screen without closing). This method allow us to track those situations:

- (void)windowDidChangeOcclusionState:(NSNotification *)notification
{
    if (self.window.occlusionState & NSWindowOcclusionStateVisible)
    {
        // Appear code here
    }
    else
    {
        // Disappear code here
    }
}
Cemen
  • 915
  • 1
  • 14
  • 17
  • Checking `window.occlusionState` doesn't seem to work for me, but `window.isVisible` does. –  May 23 '18 at 07:09
  • 1
    `-showWindow:` is not necessarily called when a Window is shown on screen. E.g. `[NSapp beginModalSessionForWindow:windowController.window]` also shows the Window on screen but never calles `-showWindow:`. Just calling this will show a window on screen: `[windowController.window orderFront:self]` and not call this method. And accesing `.window` of a `NSWindowController` to use the window directly is perfectly legal. – Mecki May 24 '19 at 13:40
  • @ErikAigner Checking `occlusionState` works fine if you want to know if the window is really visible and there is delegate callback for it that will inform you whenever this state changes. No such callback exists for `isVisible`, which you can only monitor using KVO but KVO sucks. Also `isVisible` has a different meaning, as it only tests whether the window is in the screen list of the window server; it will still say true, even if the window is entirely covered by other windows. – Mecki May 24 '19 at 13:57
0

If you really need to know the windows occlusion state go with @Cemen's answer, otherwise I'd recommend you just use the

viewWillAppear
viewDidAppear

etc. of your windows contentViewController. Just subclass that and override it there.