【问题标题】:Cocoa: NSPanel loses parent and other strange behaviourCocoa:NSPanel 失去父级和其他奇怪的行为
【发布时间】:2012-04-29 20:49:10
【问题描述】:

我有一个 NSPanel 行为异常的问题,我创建了一个 Sample App 来展示这种行为。

应用程序是从 Xcode 4 的模板生成的,只需创建一个面板,然后根据按钮按下打开和关闭它:

我观察到的奇怪行为:

  1. 在 Lion 下,第一次打开面板后,面板跟随主窗口,这是正确的行为。然而,在关闭它然后重新打开它之后,它不再跟随主窗口。
  2. 在 Snow Leopard 下,关闭面板时,主窗口也会关闭!

编辑:明确一点;我期望的行为是面板在移动主窗口时跟随主窗口;并在面板关闭并随后重新打开后实现这一点。此外,我希望面板和主窗口在 Snow Leopard 和 Lion 下的行为方式相同。

代码的重要部分在这里:

@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

注意:我在openPanel方法中添加了不必要的setParentWindow调用,以断言每次打开面板时都会重置父窗口;然而这并没有什么不同。

谁能解释一下我做错了什么?

编辑:对于实现文件中缺少的@synthesize window = _window 存在一些混淆,但我刚刚打开了另一个项目示例,该示例是我为测试内存泄漏行为而创建的,但它也不存在.我使用的是 Xcode 4.4,所以这可能是一个错误,但是我不认为项目模板已经改变。

【问题讨论】:

    标签: objective-c cocoa parent nspanel


    【解决方案1】:

    您不应该从孩子设置父子关系,而是从父级设置。 setParentWindow: 文档说:

    当它被子类的实现覆盖时,应该从子类调用此方法。否则不应调用它。

    改为使用addChildWindow:ordered:,如下所示:

    - (IBAction)openPanel:(id)sender
    {
        [[self window] addChildWindow:_panel ordered:NSWindowAbove];
        //[_panel setParentWindow:[self window]];
        [_panel makeKeyAndOrderFront:sender];
    }
    

    我没有在 Snow Leopard 下对此进行测试,但它修复了我在 Lion 上运行时的行为。

    Rob Keniger 在下面指出,在 Snow Leopard 上,您还应该在订购面板之前执行[[self window] removeChildWindow:_panel]。 (我认为这在 Lion 上也是一个好主意。)

    【讨论】:

    • 你是对的,但实际上我 am 从我的 NSPanel 子类调用 setParentWindow 并得到相同的行为。我避免使用addChildWindow,因为它会在调用时显示窗口,而setParentWindow 不会(我想在创建后的某个时间显示面板)。我不明白为什么缺少 @synthesize window - 我已经添加了它,它对行为没有影响。
    • 警告的重点不是只需要从子类调用该方法,而是子窗口不应该建立关系。您可以覆盖setParentWindow:,但这只是为了记账。如果您在-[NSWindow setParentWindow:] 上放置一个断点,您将看到在运行addChildWindow:ordered: 时它在您的面板上被调用。您只需将addChildWindow:ordered: 放入实际项目中您实际想要显示窗口的任何位置。
    • @JacquesCousteau 就在这里。您必须致电addChildWindow:,而不是setParentWindow:。我要补充一点,如果您在关闭父窗口之前从父窗口中删除子窗口,Snow Leopard 的问题将得到解决。即在面板上调用orderOut: 之前添加[self.window removeChildWindow:_panel] 调用。
    • @JacquesCousteau 解决了Lion下的问题;非常感谢。将尝试 Rob 对 Snow Leopard 的建议(在撰写本文时仍然存在)。
    • 是的(连同 Rob 的建议)解决了这两个问题;非常感谢你们。
    【解决方案2】:

    我对你的问题有点困惑。我下载并检查了你的项目。首先,不知何故你错过了应用程序主窗口的@synthesize 命令。你说你使用了模板,但不知何故它不见了。由于 AppDelegate 头文件有一个窗口的@property,因此您需要在它的实现文件中添加一个@synthesize 命令。我不确定您是如何在项目中丢失该行的,但将其添加到 @implementation 行之后...

    @synthesize window = _window;
    

    第二,为什么要陈述以下内容?

    Lion下,第一次打开面板后,面板 跟随主窗口,这是正确的行为。

    你说的没有道理。创建面板时,您使用“initWithContentRect:NSMakeRect(400, 400, 200, 100)”。请注意,您在 (400, 400) 位置创建它,大小为 (200, 100)。所以第一次打开时,它会在屏幕位置(400、400)打开。它与主窗口的位置无关。

    无论如何,添加@synthesize 部分后,我可以编译并运行应用程序而不会出现错误。对我来说,它在 10.7 中按预期工作。

    【讨论】:

    • 我已经添加了@synthesize window,但它并没有改变行为(我无法解释这条线的去向,但我很肯定我没有删除它)。无论如何,我期望的行为是当主窗口移动时,面板 跟随 主窗口。你是说你的项目现在这样做了吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多