views:

720

answers:

3

I'm a Cocoa newbie so it is likely that my approach is wrong but ..

I have an app which opens several child windows (after the main/parent window has been loaded) using NSWindowController and InitNibWIthName. This works fine.

But when I close the parent window (using the red x) these remain open and prevent the app from closing until they are closed as well. This makes sense as I am not shutting them anywhere.

But how do I do this? There must be an event that is called at this point but I can't find what it is anywhere.

Notifications such as applicationWillTerminate (and so on) are only called when the application actually is terminating not when the close button has been pressed.

I guess I'm looking for something similar to the windows WM_CLOSE type messages.

+2  A: 

windowWillClose: Apple developer docs NSWindowDelegate

Mark
WindowWillClose is never invoked (for me) but applicationwillterminate etc are.
Frank
windowWill close is from the delegate of the Window - applicationWillTerminate is on the delegate of the application
Mark
Thanks. I've got this to work. Just to elaborate for anyone new to xcode. You can get at ApplicationWillTerminate and others from the AppDelegate. To use WindowWillClose create a WindowController (.h and .m from an NSObject) class, create a new object in your nib file, select the WindowController class and use the ctrl grab to set it up as a delegate and voila.
Frank
+1  A: 

Windows and applications are not the same thing in Mac OS X.

If you have a single-window interface, with a main window and no others except for About, Preferences, etc., then you should implement applicationShouldTerminateAfterLastWindowClosed: in your application delegate and return YES. This is the only way (aside from you doing it manually) that closing a window causes the application to quit.

If you have a multiple-window interface (as in a typical document-based application), then you should make all of those windows peers to one another. Windows such as Inspectors and tool palettes should be floating panels, not regular windows. And closing the last window should never, ever quit such an app.

Peter Hosey
+2  A: 

The closest equivalent you'll find is the NSWindowWillCloseNotification posted by the window prior to its closing. You can probably get the child windows to close themselves when the parent window closes using:

NSWindow *parentWindow;
NSArray *childWindows;

NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
for (NSWindow *childWindow in childWindows) {
  [noteCenter
   addObserver:childWindow selector:@(close)
   name:NSWindowWillCloseNotification object:parentWindow];
}

If the child window will be deallocated before its parent, be sure to unregister it for notifications before that happens.

The delegate method mentioned by Mark is a convenience method for the delegate that saves them the trouble of registering for a notification they'll likely want anyway. You don't need to create a window controller just to receive that message; simply sending the window [window setDelegate:myObject] will cause myObject to receive the -windowWillClose: message if it responds to the method.

By the way, what Cocoa calls "child windows" differs from what you're thinking of. They're not addressed in the Window Programming Guide, but if you look at the documentation for the related methods on NSWindow, you'll see that they basically track the movements of their parent window, so that they move with it.

If you're coming to Cocoa from Win32 programming, you might find Apple's Porting to Mac OS X from Windows Win32 API helpful to highlight conceptual differences between Win32 and Cocoa.

Jeremy W. Sherman
Thanks for your answer (s). I've now realized that I completely misunderstood how Cocoa windows hang together which means that my original question missed the point. I started from scratch doing things in a more mac orientated way.For any windows programmers reading this, you can get at a class instantiated in Interface Builder by using an outlet such as IBOutlet NSWindow *myWindow; This seems to be assumed as so obvious (as in the world is round) that it doesn't need to be spelt out (which it did for me!!)
Frank
@Jeremy: it should read `selector:@selector(close)` instead of `selector:@(close)`
MKroehnert