How-to Present and Dismiss Secondary Views in macOS Cocoa Apps

I’ll call them “sheet”s here, but there are myriad ways to display secondary view controls for your users to focus on just that one thing they express their desire to do in your Cocoa interface. Let’s have a look!

UI Beginnings

Drag a new View Controller (I’ll call this the “secondary view” from now on) onto your storyboard, and drag an NSButton onto the newly generated view. Double-click the button and give it a label, like “Cancel”.

Now you can add whatever controls you want your sheet to contain or just follow along with the rest of the tutorial and come back later.

Opening the Sheet

Make sure that your other window contains a button that should make the sheet appear and select it.

Present it!

With the “apparition” button selected, in the “Connections” inspector (click here for a labeled image of all the inspectors), drag the “Triggered Segues” > “action” circle to the secondary view and click one of the following:

  • Show
  • Modal
  • Popover
  • Sheet

To help you decide which one to pick, I made a little screencast demonstrating the different ways to present your view:

In summary:

  • “Show” is good for asynchronous operations, where the user is allowed to bounce back and forth from the main window to the secondary window.
  • “Modal” uses the style of “Show” in that it opens a window with a dedicated title bar, but prevents the user from switching to the main window until it is dismissed via the title bar or a control like our “Cancel” button.
  • “Popover” pops out the view on top of the window from the button and can be dismissed by clicking outside of the layer or the cancel button. This is great for hiding the complexity of settings with quick access.
  • “Sheet” interrupts the user by dropping the new view from the top of the parent window and can only be dismissed via the Esc key or, of course, the cancel button (via the code of the view).

Closing the Sheet

Now we need to implement that “Cancel” button (although you can use this technique with any control on the sheet that finalizes the user’s operation), using a function called dismiss.

Make sure your new view has a view controller:

  1. Make a file in the project with a name like MySheetViewController.swift.

  2. Add a basic NSViewController declaration to the new file like so:

import Cocoa

class MySheetViewController: NSViewController {

}

3. Back in the storyboard:

  1. Select the white square inside blue circle (view controller) object above your secondary view.
  2. In the Identity inspector, write your class name (MySheetViewController here) in the “Custom Class” > “Class” text field.
  3. Open up the view controller source code in a separate Xcode window, select, and control-drag the “cancel” button from the storyboard to the class declaration. Use the popover to create an “Action” connection and give it a name like cancelSheet.

4. In the view controller class, implement the cancelSheet action like so:

@IBAction func cancelSheet(_: NSButton) {
    if presentingViewController == nil {
        // Handles Show
        view.window?.close()
    } else {
        // Handles Sheet, Popover, and Modal
        dismiss(self)
    }
}

Regarding the conditional, take note of the fact that the “Show” mode basically creates a separate window that needs to be closed, but the other styles can be dismissed because they’ve been presented from a different view; from Apple’s documentation:

In macOS, this is the universal way to dismiss a view controller, no matter how it was presented.

An Aside

The dismiss method can be called on any NSViewController , with the same results. So you can insantiate a dummy controller on the spot if you really want:

NSViewController().dismiss(self)

You just have to make sure that the view controller passed to dismiss has a corresponding presentingViewController and it’ll be dismissed.

Sources

Tags: , , ,

Published:

Updated: