Nsopenpanel as Sheet

NSOpenPanel as sheet

You need to call beginSheetModalForWindow from your panel on your window, and use a completion block:

let myFiledialog = NSOpenPanel()
myFiledialog.prompt = "Select path"
myFiledialog.worksWhenModal = true
myFiledialog.allowsMultipleSelection = false
myFiledialog.canChooseDirectories = true
myFiledialog.canChooseFiles = false
myFiledialog.resolvesAliases = true
myFiledialog.beginSheetModalForWindow(window, completionHandler: { num in
if num == NSModalResponseOK {
let path = myFiledialog.URL
print(path)
} else {
print("nothing chosen")
}
})

Presenting NSOpenPanel as sheet synchronously

Warning: I assume you are using the sandbox so NSOpenPanel & NSSavePanel are based on voodoo - their behaviour tends to vary by OS point release, the phase of the moon, whether there is any blue cheese in the house, etc. and getting them to behave normally can require a lot patience. (See this question for one person's recent pain.) Yes, I am joking - a little - but you have been warned.

Still here?

Code similar to both of the following have been known to work at some point in time, during a full moon and a prime number of seconds past midnight.

All code just typed into answer, expect typos.

First:

+ (NSInteger) myRunModal:(NSOpenPanel *)myPanel
asSheetForWindow:(NSWindow *)myWindow
{
[myPanel beginSheetModalForWindow:myWindow
completionHandler:^(NSInteger result)
{
[NSApp stopModalWithCode:result];
}
];
return [NSApp runModalForWindow:myWindow];
}

Here after the call to beginSheetModalForWindow: your thread enters a modal loop only handling events for the window. When the completion handler gets called it executed it exits that modal loop and the result is returned.

Whatever you do, DO NOT try to add this as a category to the NSOpen/SavePanel classes. That way leads to madness. Remember they are voodoo, don't mess directly with them in any way.

Second:

Use a semaphore, on the one hand this seems obvious, on the other deadlock immediately springs to mind...

+ (void) myRunModal:(NSOpenPanel *)myPanel
asSheetForWindow:(NSWindow *)myWindow
completionHandler:(void (^)(NSInteger))myHandler
{
// Use a semaphore to block thread till sheet is done
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[myPanel beginSheetModalForWindow:myWindow
completionHandler:^(NSInteger result)
{
myHandler(result);
// Unblock caller
dispatch_semaphore_signal(sema);
}
];
// Block until sheet completes
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

Here your thread waits for a semaphore to be signalled. If the system wants to execute the handler on the same thread you are deadlocked. Will it try to? Maybe.

However if you can arrange your code such that the code that needs to call NSOpenPanel is running on a thread, then arrange for the beginSheetModalForWindow: to run on the main thread with the dispatch_semaphore_wait on your thread, then a robust solution should be in your grasp.

Third:

Yes I said only two, the third option is don't even try.

HTH

NSOpenPanel Sheet

Pretty simple, it's right there in the docs though you may have missed it because the relevant method is actually part of NSSavePanel, from which NSOpenPanel inherits.

Presuming you're targeting Snow Leopard or better and thus have access to blocks, it's just a matter replacing your runModal call with this:

[openPanel beginSheetModalForWindow:mWindow completionHandler:^(NSInteger result) {

if (result == NSFileHandlingPanelOKButton) {

// Do something.
}
}];

How can I open a NSOpenPanel not modally (since I'm opening to an existing sheet)

From Presenting a Series of Sheets:

Cocoa does not support the notion of cascading, or nested sheets. Per Apple Human Interface Guidelines, “when the user responds to a sheet, and another sheet for that document opens, the first sheet must close before the second one opens.”

The doc goes on to tell you how to present a series of sheets, as the second opens it closes the first, etc.

You either need to adopt such a series or change your design so your first sheet, the one calling NSOpenPanel, is not a sheet at all.

FYI: In the sandboxed world NSOpenPanel is a very different beast while maintaining essentially the same UI. Among the changes are that NSOpenPanel is no longer a subclass of NSWindow... Tricks you might play now with NSOpenPanel may crash and burn under sandboxing, tread lightly.

Use choose file with choose file sheet (as in NSOpenPanel)

Sheets are a bit of a pain with AppleScriptObjC, but Myriad Helpers helps a lot.
To use the NSOpenSave+MyriadHelpers category, add their .h and .m files to your Xcode project using the File > Add Files to … menu item. From a new (default) AppleScript App project, for example, you could do something like:

on applicationDidFinishLaunching_(aNotification)
tell current application's NSOpenPanel's openPanel()
its setMessage:"Please select a file:"
its setPrompt:"Choose"
its setDirectoryURL:(current application's NSURL's fileURLWithPath:(POSIX path of (path to desktop)))

its setCanChooseFiles:true
its setCanChooseDirectories:false
its setAllowsMultipleSelection:true
its setAllowedFileTypes:{"png"} -- list of extensions or UTIs

its showOver:theWindow calling:{"panelCompletion:", me}
end tell
end

on panelCompletion:openItems
if openItems is missing value then -- "Cancel" button
log "Cancel"
return -- or whatever
end if
repeat with anItem in openItems as list
log anItem -- do your thing with the individual file paths
end repeat
end panelCompletion:

NSOpenPanel.runModal + NSAlert.runModal over GCD cause a hang

I think you are deadlocking the main queue because runModal() blocks the main queue in two places of your code at the same time. If your code is reentrant, that is what you get.

Possible solutions:

  1. Please avoid the use of app modal windows and use instead window modal windows a.k.a. sheets. To know how to use NSOpenPanel as a sheet, attach it to the window it pertains to. For an example, please see this answer:

NSOpenPanel as sheet


  1. You can set a flag that prevents the user from opening the NSOpenPanel if the alert is being shown, but that is ugly and does not solve any future problems that can cause other deadlocks because most probably your code is reentrant.


Related Topics



Leave a reply



Submit