NSOpenPanel not hiding window frame after "open"

I am using an NSOpenPanel to select a file and then do some post processing on it. The problem is that NSOpenPanel is not hiding its widow or window frame after the file has been selected and control has been returned to the app. There is a solid white window that replaces the panel once the file has been selected and it does not go away until after the post processing of the file has been done. It appears that the window or frame is not removed until after the main thread has completed its cycle. I have tried this with both runModal() and begin(completionHandler:) with the same results. If I perform the post processing in background, the window closes as expected, but this is not acceptable in this app as the post processing needs to be completed before any other processing is done.


Here is a simple testcase involving both the runModa and begin methods. The sleep() is used to simulate processing.


import Cocoa
class ViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        /
    }
    override var representedObject: Any? {
        didSet {
        /
        }
    }
    @IBAction func showPanel(_ sender: Any) {
  
    let Panel = NSOpenPanel()
    let result = Panel.runModal()
    print(result)
    print(Panel.url!.path)
    sleep(5)  /
    }
    @IBAction func showPanelCompletionHandler(_ sender: Any) {
  
    let panel = NSOpenPanel()
      
        panel.begin(completionHandler: ){ response in
        print("in completionHandler")
        print(panel.url!.path)
        sleep(5) /
        }
}
}

Replies

The trick is to use "orderOut:" to remove the panel before you start the synchronous post-processing. Otherwise, the frameworks do the orderOut: for you, but (as you discovered) they do it sometime "later".


Two other points:


1. Doing the post-processing synchronously is a terrible UI, because it makes your app look frozen or hung to an average user. The recommended approach is to do the post-processing on a background queue, and to add logic to your app to prevent it doing anything that conflicts until the background processing is done. If the user has to wait more than a short period (a second or so, at most), then you should also display some kind of progress window too.


2. "The sleep() is used to simulate processing."


Be careful of doing this. It doesn't simulate processing (in which the executing thread is busy), but puts the thread to sleep (which is opposite of busy). The effect on your app may be different in each case. It probably doesn't matter in this case, but it may be misleading in general.

Thank you for the reply and yes I may have to use a background thread for the processing. I had planned on using an indeterminate progress indicator. I have tried the orderOut with no success. I assume it is probably my lack of understanding on where to use it. I as calling it in the following manner:

panel.orderOut(self)


If you could point me in the correct direction for orderOut it would be appreciated. Thanks for your response,


Jim

Is your app sandboxed? In that case, I think you will have to return to the main event loop before the panel is dismissed. That's because the panel isn't actually displayed by a sandboxed app, but by a different process on its behalf, for security reasons.


The solution is still to do the post-processing separately. Once you've solved that, the panel display issue will not be a problem.