Adding a Unified `NSToolbar` to a SwiftUI window

hugh_jeremy

Hugh Jeremy

Posted on March 30, 2020

Adding a Unified `NSToolbar` to a SwiftUI window

Previously on "Adventures in SwiftUI", I discussed adding an NSTableView to a SwiftUI project. I received a follow up question from Axel:

To be honest, I don't have the faintest idea how I did this. So, I'll dig through the code and blog the process. That way, if anyone else has the same question, Google will hopefully land them here.

First stop, where do we create the NSWindow?

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var window: NSWindow!


    func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Snip
Enter fullscreen mode Exit fullscreen mode

It's declared in the AppDelegate. I can't recall if all SwiftUI apps depend on the AppDelegate pattern... If yours does not, then the following is probably still useful, but you will likely need to adapt to your circumstances.

Here is the point of NSWindow creation:

// Enclosing code snipped
window = NSWindow(
    contentRect: NSRect(x: 0, y: 0, width: 640, height: 480),
    styleMask: [
        .titled,
        .unifiedTitleAndToolbar,
        .closable,
        .miniaturizable,
        .resizable,
        .fullSizeContentView
    ],
    backing: .buffered,
    defer: false
)
window.center()
window.title = "Draft Sport"
window.titleVisibility = .hidden
window.setFrameAutosaveName("Draft Sport")
window.contentView = NSHostingView(rootView: contentView)
Enter fullscreen mode Exit fullscreen mode

The key thing to note here is the provision of the .unifiedTitleAndToolbar mask in the style mask array.

Next, we need to create the actual toolbar content.

// Enclosing code snipped
let toolbarButtons = NSHostingView(rootView: ToolbarButtons())
toolbarButtons.frame.size = toolbarButtons.fittingSize

let titlebarAccessory = NSTitlebarAccessoryViewController()
titlebarAccessory.view = toolbarButtons
titlebarAccessory.layoutAttribute = .trailing
Enter fullscreen mode Exit fullscreen mode

Finally, we add the titlebarAccessory to the NSWindow instance:

// Enclosing code snipped
window.toolbar = NSToolbar()
window.addTitlebarAccessoryViewController(titlebarAccessory)
Enter fullscreen mode Exit fullscreen mode

And voila, you've got a unified toolbar. Watch out though: I haven't gone any further with this. I imagine there are going to be all sorts of challenges developing the typical behaviour expected of a unified toolbar.

I hope this helps you Axel, and anyone else enjoying SwiftUI!

Hugh

💖 💪 🙅 🚩
hugh_jeremy
Hugh Jeremy

Posted on March 30, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related