Presenting NSOpenPanel as sheet synchronously

ε祈祈猫儿з 提交于 2020-01-02 07:54:10

问题


I have a situation where a document file relies on a link to another external media file. When the document is opened, I check to see if the referenced media file is available and if not, then the user is prompted to locate it.

Since it makes perfect sense to show an NSOpenPanel as a sheet in a document-based app, I wanted to do that but it is crucial that the current thread is blocked, so no subsequent code can be called until the new media file is chosen.

I can't find a way to present this NSOpenPanel as a sheet on the document window while blocking the current thread. Only [NSOpenPanel runModal] does what I need, but that shows as a regular dialog not a sheet.

It seems this should be possible somehow...


回答1:


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




回答2:


there is an attribute on the NSProgressIndicator that says weather or not it is indeterminate. in order for progress to show up you must set this to NO



来源:https://stackoverflow.com/questions/28478020/presenting-nsopenpanel-as-sheet-synchronously

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!