无法从另一个 class 调用 NSOpenPanel 并将其附加到其父 window

Trouble calling NSOpenPanel from another class and attaching it to its parent window

我 运行 遇到了 NSOpenPanel 的问题,并从另一个 class 调用它。

我目前有一个主要的 window,它带有一个按钮,可以打开第二个 window,它被设置为使用 ImageKit 的图像编辑器。这很好用。我还想在图像编辑器 window 打开时(由于按下按钮)启动 NSOpenPanel。基本上我想绕过让用户单击按钮打开图像编辑器,然后单击菜单中的 "Open" 或 command-O 打开图像。我们知道,如果用户打开图像编辑器,他们将需要打开图像进行编辑...我希望打开面板在显示 window 时打开。

在我的 appDelegate.m 中,我有这段代码来启动图像编辑器 window“_imageWindow”并调用 "openImage" 方法:

[_imageWindow makeKeyAndOrderFront:self];
Controller *controllerOpenImage = [[Controller alloc] init];
[controllerOpenImage openImage];

除了应该是模态的打开面板作为单独的 window 启动并且没有附加到图像编辑器 window (_imageWindow) 所以当用户选择图像时它没有打开...我尝试添加延迟以允许 _imageWindow window 时间打开无济于事。我已经尝试了 IBAction(openImage) 和 void(openImage) 有和没有发件人的相同结果...

这是在我的 Controller.m 中打开图片的代码:

- (IBAction)openImage:(id)sender
{
    // present open panel...
    NSString *    extensions = @"tiff/tif/TIFF/TIF/jpg/jpeg/JPG/JPEG";
    NSArray *     types = [extensions pathComponents];

    NSString *url=[[NSUserDefaults standardUserDefaults] objectForKey:@"photoPath"];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy"];
    NSString *yearString = [formatter stringFromDate:[NSDate date]];

    NSString *photoUrl =[NSString stringWithFormat:@"%@%@%@",url,@"/",yearString];

    // Let the user choose an output file, then start the process of writing samples
    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
    [openPanel setDirectoryURL:[NSURL fileURLWithPath:photoUrl]];
    [openPanel setAllowedFileTypes:types];
    [openPanel setCanSelectHiddenExtension:YES];
    [openPanel beginSheetModalForWindow:_imageWindow completionHandler:^(NSInteger result) {
        if (result)
        {
                // user did select an image...
                [self openImageURL: [openPanel URL]];
        }
        [_imageView zoomImageToFit: self];
    }];
}

从我的 appDelegate.m 调用时发件人是否为空,而不是从图像编辑器 window (_imageEditor) 调用时发件人具有身份,或者我要求这样做无法完成的事情。

让我们看看您的代码。首先在一些 method/function 你执行:

[_imageWindow makeKeyAndOrderFront:self];

这会访问 _imageWindow,根据通用命名约定,它可能是一个实例变量或 属性 支持变量,但也可能是一个全局变量 - 你不会说。

Controller *controllerOpenImage = [[Controller alloc] init];

这将创建一个全新的对象,在局部变量中存储对它的引用。

[controllerOpenImage openImage];

这会在这个全新的对象上调用一个方法,在该方法中您可以:

[openPanel beginSheetModalForWindow:_imageWindow ...

那么_imageWindow是从哪里来的呢?按照通用的命名约定,它是一个 instance/property 变量,如果是这样的话,它属于 Controller 实例并且 无法与 中的类似命名变量相连上面的第一行。由于您没有显示 init 方法,我们不知道您是否初始化了这个变量,如果没有,它就是 nil.

另一方面,这可能引用了一个全局变量,如果确实如此,它可能是上面第一行代码中引用的同一个全局变量。如果是这种情况,您的代码可能会工作,但不会...

因此,可以合理猜测这两个引用都是不同的,但名称相似的 instance/property 变量,它们之间没有任何联系,第二个是 nil,因此 openPanel 被传递了一个 nil window 引用并打开了一个独立的对话框。

附录

在您的第四条评论中,您的结尾是:

does that make any sense?

很遗憾没有。

当您 link 一个对象(例如您的 window)到另一个对象的 IBOutlet 时,例如您的 Controller class,那么您将在您的 NIB(IB 文档)创建的 特定实例 之间建立联系。

当系统在运行时评估您的 NIB 时,它会创建一个 window,作为 NSWindow 的一个实例,以及您的 Controller class 的一个实例,并放置一个将 specific window 实例引用到 specific ControllerIBOutlet variable/property实例.

这对您可能创建的任何其他 windows 没有影响,它们不会自动 linked 到 Controller 个实例;或在任何其他 Controller 实例上,它们不会自动 linked 到 window.

当你写:

Controller *controllerOpenImage = [[Controller alloc] init];

您正在创建一个新的 Controller 实例,它 无法连接 到您的 NIB 创建的 Controller 实例。最初,后一个实例,即您未使用的实例,可能会引用您的 window;您正在使用的前者肯定不会。

如果 您要在 NIB 中创建一个 Controller 实例,那么在您的代码中您需要使用该特定实例,而不是创建一个新实例。要使用它,您需要引用它。一种方法是将 属性 添加到引用它的 appDelegate class,在你的 NIB 中设置 link。

您在之前的评论中写道:

but I'm not really sure how to extrapolate a solution from the second comment

您提到的评论建议您通过将 window 引用作为参数传递到 Controller class 中。这是任何编程语言中非常基本的机制。在这种情况下,建议您编写一个方法,例如:

- (instancetype) initWithWindow:(NSWindow *)aWindow { ... }

并使用以下方法创建一个 Controller 实例:

[[Controller alloc] initWithWindow:_imageWindow];

听起来你需要回去研究实例、方法、参数传递的基础知识;然后是如何评估 NIB。

HTH