Reputation: 13202
I am trying to write an application that can open any folder in the NSDocument subclass but can't figure out the right Info.plist settings. It is important that my app should not use bundles, neither folders with a particular file extensions, just be able to open any folder.
What I tried:
How can I open any folder in open file dialog?
Upvotes: 7
Views: 1895
Reputation: 2657
Here's what I had to do in a new document-based application project:
Info.plist (or Target > Info > Document types)
Document type identifier: public.directory, Role: Editor, Class: $(PRODUCT_MODULE_NAME).Document
(It's important not to set the Role to Viewer, because it's documented in DocumentController.defaultType
that only Editor roles can create untitled documents, and an error is shown when trying to open one.)
AppDelegate.swift
class AppDelegate: NSDocumentController, NSApplicationDelegate {
override func runModalOpenPanel(_ openPanel: NSOpenPanel, forTypes types: [String]?) -> Int {
openPanel.canChooseDirectories = true
return super.runModalOpenPanel(openPanel, forTypes: types)
}
}
Document.swift
class Document: NSDocument {
override func makeWindowControllers() {
let windowController = ...
self.addWindowController(windowController)
}
override func read(from url: URL, ofType typeName: String) throws {
// read contents of url
DispatchQueue.main.async { [self] in
// update view controllers
}
}
override func write(to url: URL, ofType typeName: String) throws {
}
}
Upvotes: 0
Reputation: 8843
Just as an updated summary of how one does that today, here's a step-by-step guide of what I had to do:
Create an application project from the Cocoa Application template using Xcode.
Check Create Document-based Application and leave whatever it suggests there for the "Document Extension" (it will refuse to enable the "Next" button if you delete the filename extension it right here, so we'll do that later).
Click your project's icon, go to the Info tab and to Document Types.
Delete the contents of the Extensions field. Our folders don't need a particular filename suffix
Write public.folder into the Identifier field.
Under Additional document type properties in the CFBundleTypeOSTypes array add one entry, fold (just those four lowercase letters). Not sure if that is necessary, but it's at least correct.
Make sure document is distributed as a bundle is not checked.
Create an NSDocumentController
subclass containing the following method to your project. Name it E.g. ULIFolderDocumentController
.
-(void)openDocument:(id)sender { NSOpenPanel *panel = [NSOpenPanel openPanel]; [panel setCanChooseFiles:NO]; [panel setCanChooseDirectories:YES]; [panel setAllowsMultipleSelection:NO]; [panel beginWithCompletionHandler: ^( NSInteger result ) { if (result == NSFileHandlingPanelOKButton) { NSURL* selectedURL = [[panel URLs] objectAtIndex:0]; NSLog(@"selected URL: %@", selectedURL); [self openDocumentWithContentsOfURL: selectedURL display: YES completionHandler: ^(NSDocument * _Nullable document, BOOL documentWasAlreadyOpen, NSError * _Nullable error) { NSLog(@"%spened document %@ (%@)", (documentWasAlreadyOpen? "Reo" : "O"), document, error); }]; } }]; }
-init
method that loads your subclass instead of NSDocumentController
. This is easy, just request the shared object:[ULIFolderDocumentController sharedDocumentController]; // Override system's NSDocumentController with ours.
Upvotes: 3
Reputation: 13202
For completeness here are some more details to @iKenndac's answer:
In IB check which method of First Responder is associated with the File / Open... menu item. In my case it was openDocument:
. Implement this method in the 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];
}
}];
}
You still need to define a Document Type in the Info.plist, setting the Identifier (LSItemContentTypes) field to public.folder
.
Upvotes: 6
Reputation: 18776
You probably can't do this without writing some custom code.
You need to present an NSOpenPanel
manually, like this:
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];
An open panel presented in this way will let the use choose any directory they wish. You can implement NSOpenPanel
's delegate methods to validate each folder and en/disable if if you need to.
Upvotes: 3