使用 NSDocument 打开任何文件夹

Posted

技术标签:

【中文标题】使用 NSDocument 打开任何文件夹【英文标题】:Open any folder with NSDocument 【发布时间】:2012-12-20 10:15:54 【问题描述】:

我正在尝试编写一个可以打开 NSDocument 子类中的任何文件夹但无法确定正确的 Info.plist 设置的应用程序。重要的是,我的应用程序不应使用捆绑包,也不应使用具有特定文件扩展名的文件夹,而只能打开任何文件夹。

我尝试了什么:

如果我将文档类型扩展名设置为空字符串,则文件打开面板不允许选择任何文件 如果我将文档类型扩展名设置为 *,则文件打开面板会启用所有文件,但不会启用文件夹:文件夹会像在 finder 中一样打开 如果我将文件夹扩展名设置为文档类型扩展名,我可以在文件打开对话框中将文件夹作为文档打开(这是我想要的),但我的解决方案仅限于具有该扩展名的文件夹 通过将 OSType 设置为“fold”,将文档类型标识符或名称设置为“public.folder”等,正如我在论坛中看到的那样,对我没有明显的影响。

如何在打开文件对话框中打开任何文件夹?

【问题讨论】:

【参考方案1】:

为了完整起见,这里是@iKenndac 回答的更多细节:

在 IB 中检查 First Responder 的哪个方法与 File / Open... 菜单项相关联。就我而言,它是openDocument:。在 AppDelegate 中实现这个方法:

-(void)openDocument:(id)sender

    NSOpenPanel *panel = [NSOpenPanel openPanel];
    [panel setCanChooseFiles:NO];
    [panel setCanChooseDirectories:YES];
    [panel setAllowsMultipleSelection:NO];

    [panel beginSheetModalForWindow:nil
                  completionHandler:^(NSInteger result) 
                      if (result == NSFileHandlingPanelOKButton) 
                          NSURL* selectedURL = [[panel URLs] objectAtIndex:0];
                          NSLog(@"selected URL: %@", selectedURL);
                          NSError* error = nil;
                          [[NSDocumentController sharedDocumentController] 
                              openDocumentWithContentsOfURL:selectedURL 
                                                   display:YES 
                                                     error:&error];
                      
                  ];

您仍然需要在 Info.plist 中定义一个 Document Type,将 Identifier (LSItemContentTypes) 字段设置为 public.folder

【讨论】:

是的,但我认为你应该在 NSDocumentController 子类中这样做。【参考方案2】:

如果不编写一些自定义代码,您可能无法做到这一点。

您需要手动提交NSOpenPanel,如下所示:

NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:NO];
[panel setCanChooseDirectories:YES];

[panel beginSheetForDirectory:nil
                         file:nil
               modalForWindow:[self window]
                modalDelegate:self
               didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
                  contextInfo:nil];

以这种方式呈现的打开面板将让用户选择他们想要的任何目录。您可以实现NSOpenPanel 的委托方法来验证每个文件夹并在需要时启用/禁用。

【讨论】:

【参考方案3】:

正如对今天如何做到这一点的更新摘要一样,这里是我必须做的分步指南:

    使用 Xcode 从 Cocoa 应用程序 模板创建应用程序项目。

    选中创建基于文档的应用程序,并为“文档扩展名”留下任何建议(如果您正确删除文件扩展名,它将拒绝启用“下一步”按钮在这里,所以我们稍后会这样做)。

    点击您的项目图标,转到信息选项卡和文档类型

    删除 Extensions 字段的内容。我们的文件夹不需要特定的文件名后缀

    public.folder 写入 Identifier 字段。

    CFBundleTypeOSTypes 数组的 Additional document type properties 下添加一个条目,fold(仅这四个小写字母)。 不确定是否有必要,但至少是正确的。

    确保文档以捆绑包形式分发检查。

    为您的项目创建一个包含以下方法的NSDocumentController 子类。命名它例如ULIFolderDocumentController.

-(void)openDocument:(id)sender NSOpenPanel *panel = [NSOpenPanel openPanel]; [面板 setCanChooseFiles:NO]; [面板 setCanChooseDirectories:YES]; [面板 setAllowsMultipleSelection:NO]; [面板 beginWithCompletionHandler: ^( NSInteger 结果) 如果(结果 == NSFileHandlingPanelOKButton) NSURL* selectedURL = [[panel URLs] objectAtIndex:0]; NSLog(@"选择的 URL: %@", selectedURL); [self openDocumentWithContentsOfURL: selectedURL 显示:是 completionHandler: ^(NSDocument * _Nullable 文档, BOOL documentWasAlreadyOpen, NSError * _Nullable 错误) NSLog(@"%spened 文档 %@ (%@)", (documentWasAlreadyOpen? "Reo" : "O"), document, error); ]; ];
    在您的应用程序委托的-init 方法中添加一行来加载您的子类,而不是NSDocumentController。这很简单,只需请求共享对象:
[ULIFolderDocumentController sharedDocumentController]; // 用我们的覆盖系统的 NSDocumentController。
    试试吧! :)

【讨论】:

2022 年更新: 这基本上仍然有效,但有一些细微差别: - 您需要在 Document Types 部分中进行一些更改现在在 Imported Type Identifiers 部分。看起来标识符在两个部分之间也是重复的。 - 我没有设法添加其他文档类型属性,但它似乎仍然有效。 - 我没有在任何地方发现 Document 以捆绑包形式分发,但它似乎仍然有效。所以大多数情况下,只要尽可能按照说明进行操作,它们仍然可以工作! 更多注意事项: - 在 NSDocument 子类中,将 read(from data: Data, ofType typeName: String) 方法替换为 read(from url: URL, ofType typeName: String) 方法,因为您将从磁盘上的文件夹中读取,而不是使用 macOS为您读取数据 - 在 NSDocumentController.h 中,Apple 建议从 applicationWillFinishLaunching: 实例化您的自定义文档控制器,而不是从 init: 实例化 - 不确定有什么区别

以上是关于使用 NSDocument 打开任何文件夹的主要内容,如果未能解决你的问题,请参考以下文章

NSDocument 应用程序和文件扩展名

如何在 NSDocument 加载之前拦截 openDocument 的结果?

NSDocument 呈现的ItemDidChange 每秒调用一次

设置 NSDocument 以保存非本机类型

NSDocument 保存音频和视频文件

基于 NSDocument 的应用程序:在 NSSavePanel 中选择默认文件类型