I have an issue with an NSPanel
acting strangely and have created a Sample App to demonstrate this behaviour.
The App was generated from Xcode 4's template and simply creates a panel and then opens and closes it based on button presses:
The strange behaviour I have observed:
EDIT: Just to be clear; the behaviour I expect is for the panel to follow the main window around when the main window is moved; and for that to be true after the panel is closed and subsequently re-opened. Also I expected the panel and main window to behave the same way under Snow Leopard and Lion.
The important part of the code is here:
@implementation MyAppDelegate
- (void)dealloc
{
[_panel release];
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_panel = [[NSPanel alloc]
initWithContentRect:NSMakeRect(400, 400, 200, 100)
styleMask:NSUtilityWindowMask|NSClosableWindowMask|NSTitledWindowMask
backing:NSBackingStoreBuffered
defer:YES];
[_panel setTitle:@"A Panel"];
}
- (IBAction)openPanel:(id)sender
{
[_panel setParentWindow:[self window]];
[_panel makeKeyAndOrderFront:sender];
}
- (IBAction)closePanel:(id)sender
{
[_panel orderOut:sender];
}
@end
Note: I added the unnecessary setParentWindow
call to the openPanel
method to assert that the parent window is reset every time the panel was opened; it made no difference however.
Can someone please explain what I'm doing wrong?
EDIT: There is some confusion about the missing @synthesize window = _window
from the implementation file, but I have just opened another project sample that I created to test for memory leak behaviour and it's not in there either. I am using Xcode 4.4, so it's possibly a bug in that, however I don't think the project templates have changed.
You're not supposed to set the parent-child relationship from the child, but from the parent. The setParentWindow:
docs say:
This method should be called from a subclass when it is overridden by a subclass’s implementation. It should not be called otherwise.
Instead, use addChildWindow:ordered:
, like so:
- (IBAction)openPanel:(id)sender
{
[[self window] addChildWindow:_panel ordered:NSWindowAbove];
//[_panel setParentWindow:[self window]];
[_panel makeKeyAndOrderFront:sender];
}
I didn't test this under Snow Leopard, but it fixes the behavior for me when run on Lion.
Rob Keniger notes below that on Snow Leopard you should also do [[self window] removeChildWindow:_panel]
before ordering the panel out. (I assume this is also a good idea on Lion.)