Search code examples
swiftmacosnsalert

Programmatically dismiss modal dialog in MacOS


I have a warning dialog from a MacOS application's AppDelegate that needs to be updated with new information. When the new information is available, I want to programmatically dismiss the old dialog and present the new one. I have tried this two ways, both with problems:

  1. Using alert.runModal()

    If I use the above, a modal is presented as desired. I can then dismiss the dialog later with lockWarningModal.window.close(), it works to make the old dialog disappear, but it freezes the UI, so I can no longer interact with it. I am guessing this is because alert.runModal() is synchronous, and the main thread is still blocked. However, I don't know how to release this.

  2. Using alert.beginSheetModal(for: NSApplication.shared.windows.last!) { (response) in }

    If I use the above and dismiss the dialog with NSApplication.shared.windows.last!.endSheet(alert.window), then this solves the UI freeze problem. However, the dialog is attached to the main application window and is not brought to the front as a modal.

How can I achieve a modal dialog that is programmatically dismissible?


Solution

  • You can't stop a model event loop (or alert sheet) by simply closing its window. In fact, using the modern NSAlert API, you should never have to close or order out the window—the framework handles this for you.

    For an alert started with runModal() use NSApplication's abortModal(), stopModal(), or stopModal(withCode:). After runModal() returns, send the alert window an orderOut(nil) to remove it.

    For an alert sheet that executes a completion block afterwards, use NSWindow's endSheet(_) or endSheet(_:returnCode:). The alert will be automatically removed after your completion block executes.