Skip to content

Instantly share code, notes, and snippets.

@mackuba
Last active January 21, 2024 13:36
Show Gist options
  • Save mackuba/e6e95e941b0748caf791ae6e76f458c4 to your computer and use it in GitHub Desktop.
Save mackuba/e6e95e941b0748caf791ae6e76f458c4 to your computer and use it in GitHub Desktop.
How to create toolbar buttons in AppKit

Creating toolbar buttons

Recently I was writing a post about NSButton styles on my blog, and when I got to the part about toolbar buttons (Textured Rounded), I realized that I actually had no idea how to create a toolbar in AppKit, so I had to do some research first. I found some help in this sample project from Apple and this project on GitHub, and then I did some experiments with different combinations of things to see what works where.

This could probably be a whole separate blog post on its own (and maybe I'll make this into one), but for now I've put it here to not make that main article even longer.

Here's what I've found:

There are a few different kinds of buttons you can put in the toolbar:

  1. A large, plain, unbordered icon - this was an old toolbar style that started disappearing from macOS long ago (replaced by real buttons) and was practically gone by the time of macOS Sierra, and only survived in preferences windows
  2. A bordered toolbar item - option added in Catalina that automatically creates a textured rounded button for you
  3. A toolbar item with a custom view which is a textured rounded button
  4. A toolbar item with a custom view which is a popup, search field, segmented control etc.

There are also two ways you can build a toolbar with all of these:

  1. On the storyboard
  2. In code

Creating a toolbar on the storyboard

To create a toolbar on the storyboard, drag a toolbar from the Object Library into your window - it will automatically snap to the top of it. Now, double click the toolbar to show a sheet with "Allowed Toolbar Items" and "Default Toolbar Items" - you only edit the toolbar elements inside that sheet.

You can throw out the few standard items added there automatically, and then drag items from the Library into the top area (Allowed Items) - either an "Image Toolbar Item" (this will create an NSToolbarItem with an image), or a specific control like the Textured Rounded Button or a Segmented Control (this will create a toolbar item with a custom view). Then configure the toolbar item and/or the control inside, and also drag it into the bottom bar (Default Items) if it should appear by default.

Creating toolbar items in code

To build a toolbar in code, make your class implement the NSToolbarDelegate protocol and assign it as the toolbar's delegate. You will need to implement 3 delegate methods:

toolbarAllowedItemIdentifiers(_:)

  • this returns a list of string-based identifiers for all available items (just make up any unique name for each item)

toolbarDefaultItemIdentifiers(_:)

  • returns a subset of the above list that is displayed by default before the user customizes the toolbar

toolbar(_:, itemForItemIdentifier:, willBeInsertedIntoToolbar:)

  • this is where you create and return an NSToolbarItem for the passed identifier
  • set the item's labels, target/action and either an image & isBordered or a custom view like an NSButton that you create in code set in the view property

How to do this right

Now, the question is: since you have a few different ways to build a toolbar, plus also in any case you can set the sizing in various ways, how exactly should you do it to make it look like in system apps?

There are a few things here to keep in mind, based on the docs and what I've found:

  • the bordered toolbar item option was added in Catalina, but it only works there when set in code - the storyboard option only works in Big Sur+
  • bordered toolbar item buttons and explicitly built buttons look slightly different - the label below highlights on click for bordered toolbar items, but doesn't highlight for custom buttons
  • you can explicitly set a size for the item and/or button, but if you keep it at auto it displays as you'd expect on Mojave and above (but not in older versions, where in most cases it shows up weird, either too small or too large)

I can't really tell if Apple's apps build toolbars in code and how they use NSToolbarItem, since it's not a view object, but I could at least check how the result looks visually - and the answer is… each app does it differently :D

On Catalina, a few apps like Notes, Finder, Photos have buttons with ~39pt size, which is what I get if I leave the size at auto or set it to 40. Mail has 40pt buttons, Photos has 41pt buttons and Safari has 38pt buttons. They also differ in how the labels work on click - in Mail they highlight, in Finder they don't. The rest doesn't have an option to show labels.

On Big Sur, each icon/button generally has a different size depending on the size of the specific symbol used (the padding around the symbol tends to stay the same). Although in Mail all buttons are of the same, slightly larger size (they're actually one-segment segmented controls).

So I can only say what I think is the recommended way, based on two things:

  • that the bordered property was added fairly recently, which means Apple probably wants you to use it
  • that the minSize/maxSize properties were deprecated in Big Sur, which means Apple probably doesn't want you to use them

My recommendation

So here's my recommendation for how you should do it, depending on which macOSes you want to support:

Only Big Sur & Monterey

  • build the toolbar on the storyboard or in code, how you prefer
  • for single buttons use bordered image toolbar items ("Image Toolbar Item" with bordered = on or NSToolbarItem with image and bordered set)
  • use SF Symbols for icons
  • if you want to use some advanced symbols from Monterey, build the toolbar in code and have if #available code paths
  • do not set minSize/maxSize

Catalina and above

  • build the toolbar in code
  • for single buttons use bordered image toolbar items (NSToolbarItem with image and bordered set)
  • have if #available code paths to set a classic image icon for Catalina and SF Symbols icon(s) for Big Sur & Monterey
  • do not set minSize/maxSize

Mojave and above

  • build the toolbar in code
  • have separate if #available code paths:
    • for Big Sur & Monterey, use bordered image toolbar items with SF Symbols (auto size)
    • for Catalina, use bordered image toolbar items with an image you provide (auto size)
    • for Mojave, use toolbar items with view set to a manually built NSButton with a .texturedRounded bezel (button's bordered = true), also auto size

Older than Mojave

  • same as above, but in the custom NSButton code path for Mojave and earlier also set both minSize and maxSize on the item to 40 x 23 (height doesn't matter as long as it's not too large)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment