Toolbars have been an integral part of macOS since the beginning. They provide the opportunity to display frequently used controls to the user. Where older macOS applications might have presented a pallet with certain options, modern macOS apps tend to use toolbars. While Apple provides an extensive guide for macOS toolbars, it was last updated in February 2009.
In this tutorial, we will build two types of toolbar (an icon toolbar and one using custom controls), experiment with hooking up toolbar items and their actions, and as a bonus item, look into the toolbar mode of NSTabView.
tl; dr: NSToolbar does show its age; it does not play too well with storyboards, and after writing this post, I am really hoping for a major update soon.
Basic toolbar in in storyboard
0) This creates the most basic toolbar.
Create a new macOS application. Drag a toolbar into the WindowController scene. Build and run.

This is your basic toolbar. The Colours and Fonts items are fully functional, the Print button is not.
0a) In the ‘View’ menu, you will find two relevant items: Show/Hide Toolbar and Customize Toolbar.
Customize gives you the following sheet:

In other words, it shows the toolbar as it is right now, the items that are allowed, and gives the user the opportunity to change settings – icons and text, icon only, text only, plus the option of arranging the order of items.
0b) The allowed items correspond to the setup of the toolbar in Interface Builder:

When you search the ObjectLibrary, you will see that the only other available item is an ‘Image Toolbar Item’
This is radar-worthy: the NSToolbarItem.Identifier struct contains not only these, but two deprecated items (fair enough), as well as two relatively new items:
static let toggleSidebar
//The standard toolbar item identifier for a sidebar. It sends toggleSidebar: to firstResponder. (macOS 10.11+)
static let cloudSharing
//(macOS 10.12+); no discussion available
which are not exposed in IB and thus cannot be created easily.
1) Let’s create a custom toolbar with custom ImageToolbar Items.
1a) Add three images to your application’s asset catalogue. (Mine are a butterfly, fish, and lizard). You can add toolbar items in two ways: either double-click the toolbar and drag the item to the ‘allowedItems’ area, or drag it into the toolbar in the document outline (see image above). By default, they appear as ‘toolbar item’ in the toolbar.
Since we won’t work with them, remove the Fonts and Print items (but keep the colours for now).
In the attributes inspector, add your icons to the three new elements.
Rearrange the default toolbar so that your custom items appear in it. Here is the first quirk of toolbars: if you name them in the document outline, both label and palette label will remain as ‘toolbar item’; you need to rename them in the Attributes Inspector. Here’s the second quirk: The ‘allowedItems’ listing uses the Palette Label (here: Butterfly), while the default items (and the toolbar shown to the user) uses the Label (‘Fly’).

Right now, I don’t see much reason to use different labels, but if you are using this style of control, you should set both. (When using custom controls, the ability to show a label in the customisation area becomes more interesting.)
1b) Right now, our custom items are greyed out since they have no actions associated with them. The easiest way to change that is to create IBActions in a new WindowController subclass and connect them directly. Create a ToolbarWindowController subclass of NSWindowController, assign it to your window’s class in the storyboard, and drag from each toolbar item to the windowController.
@IBAction func fly(_ sender: NSToolbarItem) {
print("Fly away")
}
@IBAction func swim(_ sender: NSToolbarItem) {
print("Swimming in circles")
}
@IBAction func bask(_ sender: NSToolbarItem) {
print("I'm enjoying my rock")
}
Build and Run.
1c) So far, so good. Our toolbar items are connected to actions. The bad news is that these actions live in the WindowController. Back in the days of macOS 10.9 and earlier, WindowControllers played a much bigger role in app design than they do now; but in modern apps – particularly if, like I do, you use storyboards – ViewControllers tend to be where code lives, which makes for more modular, more flexible app design.
The alternative to a direct connection is to declare these actions in the ViewController and connect them via first responder. Copy the methods above, and paste them into the ViewController subclass.
Drag a label into the ViewController scene, widen it to sentence length, create an outlet for it: @IBOutlet weak var resultLabel: NSTextField!
and change the ‘print’ in the statements above to resultLabel.stringValue =
Now disconnect the toolbar buttons from the actions in the ToolbarWindowController and drag to the FirstResponder and find the function names (fly/swim/bask) and connect to those.
Comment out the methods in the WindowController to make this work, otherwise *they* will be called.
Build and Run. The label text will change.
So that’s a basic working toolbar. Next up: toolbar item properties.
Labels, images, selectability, and more.
1d) Let’s take a look at the standard toolbarItem attributes.

ImageName, Label, and Palette Label we have dealt with. Identifier we will leave alone for now, since this is a predetermined toolbar (we create the items in IB). Tag is an oddity: by default, all NSToolbarItem tags are -1. This includes the space/flexible space. The impression this gives is that – unlike other controls – it is not common to refer to toolbarItems by tag. (Expectation was that the non-selectable ones – space/flexiSpace – are -1, and the rest would be 0 by default.)
Auto-generated Identifiers are long and cumbersome Strings, so I recommend entering a value (Lizard or BD921647-5EA6-4762-9FFF-551666D60CEC ? You decide.)
This leaves three items that may be of interest: Priority, Autovalidates, and Selectable.
Autovalidates is selected by default. Comment out ‘Autovalidates’ for the butterfly button and comment out the ‘Fly’ action in the viewController. Build and run. The button appears enabled, shows the animation for being clicked, but doesn’t do anything. Enable ‘Autovalidates’ and it will be greyed out and unresponsive.
(Don’t forget to uncomment the fly action again.).
This is a good setting, a very useful one; under most circumstances you will want it set to true.
Now set ‘selectable’ for each of the three buttons. The button that was selected last now shows a slight grey highlight. When, like here, you are using a group of toolbar items to switch modes, that, too, is a useful setting. When, on the other hand, the items are one-shot actions, you want to uncheck selectable.

If you are using selectable toolbar items, you can use NSToolbar’s func selectedItemIdentifier() to find out which item is currently selected. (this returns an optional NSToolbarItem.Identifier)
This leaves the trickiest setting, priority. It’s more of an enigma. The documentation only has an entry for ‘visibility priority’ which says
The receiver’s visibility priority and contains the discussion Possible values are described in Item Priority. (Spoiler: there is no entry in NSToolbarItem for ‘ItemPriority’.)
NSToolbarItem.VisibilityPriority is a struct, and gets weirder still:
it has two initialisers, init(Int) and init(rawValue: Int). They seem to do the same thing, and the first may be an artefact of earlier Swift syntax; for consistency’s sake I would recommend using the second. It also has four type properties:
– high
– low
– standard
– user
and the discussion makes it clear what this is about: the likelyhood of an item being pushed to the overflow menu when the window is too narrow to acommodate all of the items.
I set the priorities of mine to fly: 0, swim: 5, bask: 20. When narrowing the window in the setup pictured above, they were pushed out in the following order: Fly, Colors, Swim. Colors always beats fly, so: items of equal priority get booted from trailing to leading (have not tested with right-to-left language, but I’m assuming this), items with other priorities get booted in order of priorities.
I am still somewhat unclear where ‘user’ priority comes in here, or whether that simply IS a reflection of ‘user put these items to the left side of the window; we assume they want to see them’.
When you access the priorities, they will be displayed as VisibilityPriority(rawValue: 5) etc, so the low/standard/high seems to be an internal calculation.
1e) The toolbar itself exposes no interesting settings in IB, but there are a few properties that are worth taking note of:
var allowsExtensionItems: Bool //false by default. If set to true, it allows the toolbar to add ActionExtensions dynamically, the most common one being the ‘Share’ extension.
var identifier: NSToolbar.Identifier
//in case you want more than one toolbar in your application – if you have a photo browsing and photo editing mode, for instance, you might want two context-specific toolbars. This is how you get to know them.
var items: [NSToolbarItem]
var visibleItems: [NSToolbarItem]?
(these are read-only)
var configuration: [String: Any]
provides another quirk; mine printed out as
Optional([“TB Icon Size Mode”: 1, “TB Size Mode”: 1, “TB Display Mode”: 1, “TB Is Shown”: 1]) even though the documentation states Contains displayMode, isVisible, and a list of the item identifiers currently in the toolbar. I’m suprised that icon size mode and size mode are separate entries, but I’m not seeing the ‘list of item identifiers’. (This is a read-only property with a func setConfiguration([String : Any])
method to complement it.) Since NSToolbar has an autosavesConfiguration property, I would have expected the itemIdentifiers to be included. With ‘autosaves configuration’ the user-determined toolbar is preserved between app launches, even in debug mode, so there may be something else going on here, and indeed: if you customize the toolbar, items and visible items get added to the configuration dictionary, but only then.
NSToolbar has, as so many items in the Cocoa frameworks do, a delegate property.
NSToolbarDelegate: not working as expected; bug report pending
2) We have toolbar items; they do stuff. This is enough to get you up and running for draft purposes, but you can do more. Let’s look at the delegate first.
weak var delegate: NSToolbarDelegate? { get set }
The documentation says: Every toolbar must have a delegate, which must implement the required delegate methods.
Spoiler: In Swift, this is an optional delegate… and the toolbar has been working just fine without one in our storyboard.

Bug Warning: Read this warning before following the tutorial
To make the tutorial easier to follow, and in the hope that different configurations do not suffer from the same bug, I shall pretend that if you use the code I write below, your app will work as advertised. Under 10.12.6/Xcode 9.2, it did not. Whether delegate methods are called or not appears to be entirely random (I am filing a radar for this); it does not reflect on the code. This does, of course, make it completely impossible to use a storyboard-generated toolbar in production if you want to add new items in code (if you don’t, and you can set up all items you wish to use in IB, you’re fine: toolbars are great).
If the code you find below does not work, I am sorry, but you need to yell at Apple, not at me.
(The next two screenshots are *exactly the same code* running on different iterations. All I’ve done was press the ‘run’ button several times util I could get my screenshot.)
I apologise for the lousy tutorial experience – maybe it will work – but I hope that this will get fixed before you read the tutorial; once I am satisfied that it is fixed for good, I will remove the warning. For now, I shall pretend that the code works every time; it most certainly works for toolbars created in code, but there are some additional quirks regarding storyboards that I found well worth documenting; and ultimately, my goal is to work with storyboards.


For the purposes of this tutorial only, I will use the ToolbarWindowController since it’s much easier to set up; in a real app I would either use a dedicated ToolbarDelegate class (if it has lots of work to do/I use several view controllers with the same toolbar) or my root viewController (if there’s only one.)
2) Make the ToolbarWindowController conform to the NSToolbarDelegate protocol, and set it as the toolbar’s delegate:
override func windowDidLoad() {
super.windowDidLoad()
window?.toolbar?.delegate = self
//see warning above: under 10.12.6/Xcode 9.2, the delegate is always set correctly, but the delegate methods are only called some of the time.
}
If you’re wondering what the methods are that the delegate has to implement, you’re in good company. The NSToolbarDelegate documentation identifies as: A set of optional methods implemented by toolbar delegates to configure the toolbar and respond to changes.
2a) The following has a curious relationship with the toolbar we’ve set up in storyboards: it’s cumulative. After implementing this, my expectation was that I would get only a single item.
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining allowed items")
return[NSToolbarItem.Identifier.showFonts]
}
Instead, my customize Toolbar area now looks like this:

This method gets called only when you choose the ‘customize Toolbar’ menu item.
Adding items that you have already added in InterfaceBuilder – as long as they are part of the standard set – results in only one copy of your item.
2b) Comment out the method above, and try the following for size:
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining default items")
return[NSToolbarItem.Identifier.showFonts]
}
This, too, is cumulative: now the Fonts item gets added to the toolbar on startup; the items set in IB (respectively set in earlier customisation and autosaved in the configuration) are still there.

2c) Maybe unsurprisingly,
func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining selectable items")
return[NSToolbarItem.Identifier.showFonts]
}
is ALSO cumulative: even though I have commented out 2b); the Fonts item turns up in my toolbar, and it’s selectable. It cannot, however, be customized, and if the user removes it from the toolbar and saves that configuration, it will be lost forever. This looks like an interesting source of bugs.
2d) Leave the function above, and add
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem{
print("Toolbar will add item \(toolbarItem.itemIdentifier)")
}
}
This gets called for every toolbar item.
func toolbarDidRemoveItem(_ notification: Notification) works in a similar fashion; right now it gets called only when the user customises the toolbar.
Usually, notifications are more finely grained and allow for validation: willAddItem/didAddItem and willRemoveItem/DidRemoveItem as well as having a shouldAdd/shouldRemove option. willAdd/didRemove feels a little sparse.
2e) Remove the font item in the customize Toolbar dialog, comment out 2c) and run to ensure we no longer have a Font item.
There is one last delegate method that’s worth looking at:
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
print("item for identifier called")
if itemIdentifier == NSToolbarItem.Identifier.showFonts {
let toolbarItem = NSToolbarItem(itemIdentifier: .showFonts)
toolbarItem.visibilityPriority = NSToolbarItem.VisibilityPriority(rawValue: 40)
return toolbarItem
} else {
return nil
}
}
This is the bit where you read which identifier is being requested and you return an appropriate item. It is also the only method that allows you to customise toolbar items.
This, too, turns out to be cumulative: your showFonts item will have the settings you requested, but the other items are still being created with their default storyboard settings. My expectation, in line with other delegate methods in other protocols was that this particular (nonsensical) implementation would mean that only the showFonts item gets created, but this is not the case.
The problem is that so far, even when the other delegate methods work, it has not been called once with the storyboard setup.
Calling self.window?.toolbar?.insertItem(withItemIdentifier: .showFonts, at: 0)
in the ToolbarWindowController’s ‘windowDidLoad’ method inserts the item, but also does not fire the delegate method.
This means that there is no chance to create new NSToolbarItems and customize them (giving them icons, titles, priorities etc) other than in willAddItem. To make this work you need to enable toolbarAllowedItemIdentifiers (2a above) as well as customising toolbarWillAddItem
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem{
if toolbarItem.itemIdentifier == .showFonts{
toolbarItem.visibilityPriority = NSToolbarItem.VisibilityPriority(rawValue: 40)
}
print("Toolbar will add item \(toolbarItem.itemIdentifier)")
}
}
2d) What if you want a pony? Or rather, what if you want to add an NSToolbarItem that is not part of the set available in the Cocoa framework? What if you want to create a NSToolbarItem with the identifier “Pony”?
In this version, the version that’s working with storyboards, you seem to be out of luck.
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining allowed items")
return[NSToolbarItem.Identifier.showFonts, NSToolbarItem.Identifier(rawValue: "Pony")]
}
plus
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem{
if toolbarItem.itemIdentifier == .showFonts{
toolbarItem.visibilityPriority = NSToolbarItem.VisibilityPriority(rawValue: 40)
}
if toolbarItem.itemIdentifier.rawValue == "Pony"{
toolbarItem.image = #imageLiteral(resourceName: "pony")
print("I want a pony")
}
print("Toolbar will add item \(toolbarItem.itemIdentifier)")
}
}
behaves oddly: You will find ‘I want a pony’ in the console, but no new items in the allowed items area of toolbar customisation. You can add it in windowDidLoad with
self.window?.toolbar?.insertItem(withItemIdentifier: NSToolbarItem.Identifier(rawValue: "Pony"), at: 0)
which gives you the item, but it _still_ does not appear in the allowedItems area (or the default area if you’re using 2b as well). If itemWithIdentifier
is called (it happened a couple of times for me), this works perfectly fine.
And that, as far as I’m concerned, seems to be the end of the line. If you want to set up your toolbar using storyboards, creating and adding new items in code appears to be impossible at this point in time. (Theoretically it should work. It’s just not production-ready right now.)
Creating a toolbar in code
3) I think the storyboard option is wonderful – it’s easy to set up, very easy to control your items, and for most purposes, will work marvellously well. But if you really want a pony – a way of adding NSToolbarItems dynamically – right now, a toolbar in code appears to be the only way.
3a) Create a new WindowController, call it ‘CodeWindowController’ and set it as the initial windowController of your app. Create an NSWindowController subclass called CodeWindowController and set it to the windowController’s class. Make it conform to NSToolbarDelegate and set it as the toolbar’s delegate in windowDidLoad.
Set the viewController’s class to ViewController, add a label, and connect it to the resultLabel outlet.
We are going to copy much of the code from the ToolbarWindowController, but first, we need to gather the information we need; and the best way to do that is to create a quick struct (you can use a dictionary, but that’s very Objective-C: I like Swift’s strong typing.)
(I will be using the same string as the item’s label, palleteLabel and identifier, so I only need one here)
struct ToolbarItemTemplate {
var name: String = "Pony"
var image: NSImage? = #imageLiteral(resourceName: "pony")
var action: Selector = #selector(CodeWindowController.squee)
}
3b) Set up the CodeWindowController. I’m reproducing all of it below, comments inline.
class CodeWindowController: NSWindowController, NSToolbarDelegate {
var toolbar: NSToolbar?
var toolbarTemplates: [ToolbarItemTemplate] = [ToolbarItemTemplate()]
override func windowDidLoad() {
super.windowDidLoad()
setUpToolbar()
}
func setUpToolbar(){
toolbar = NSToolbar(identifier: NSToolbar.Identifier(rawValue: "CodedToolbar"))
toolbar?.allowsUserCustomization = true
toolbar?.delegate = self
self.window?.toolbar = toolbar
}
If you set up a toolbar in code, the next four functions are mandatory; your app will not function without them.
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining allowed items")
var allowedIdentifiers: [NSToolbarItem.Identifier] = []
for item in toolbarTemplates {
let id = NSToolbarItem.Identifier(rawValue: item.name)
allowedIdentifiers.append(id)
}
allowedIdentifiers.append(NSToolbarItem.Identifier.space)
allowedIdentifiers.append(NSToolbarItem.Identifier.showColors)
return allowedIdentifiers
}
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return toolbarAllowedItemIdentifiers(_:toolbar)
}
func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return []
}
In other words, default and allowed identifiers are identical in this example, and no items are selectable; you can customize at will.
The following function comes with the caveat that under macOS 10.12, it works cumulatively. You can, right now, remove the ‘switch itemIdentifier’ part, which theoretically should fall back to ‘return nil’, but if you remove the handling of .showColors from this function, you will still get a fully-formed NSToolbar item. This does not come as a great surprise – after all, toolbar items were created in the storyboard verion without calling this function – but it’s also the sort of thing that could break with any future macOS update, so just because it works doesn’t mean that your code is correct.
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
switch itemIdentifier{
//handle any cases you have added to defaultItemIdentifiers
case .space, .showColors: return toolbarItem
default:
let wantedItem = itemIdentifier.rawValue
let templateArray = toolbarTemplates.filter{$0.name == wantedItem }
if !templateArray.isEmpty{
let template = templateArray[0]
toolbarItem.image = template.image
toolbarItem.label = template.name
toolbarItem.paletteLabel = template.name
toolbarItem.action = template.action
return toolbarItem
}
}
//this should never be called if you're handling all identifiers correctly above, but it's structurally necessary
return nil
}
@objc func squee(){
print("I has a pony")
}
}
3c) Build and Run. Click on the ‘Pony’ button. Squee.

3d) It’s worth pointing out that by default, NSToolbarItem’s allowsDuplicatesInToolbar is ‘true’. And while Cocoa handles things smartly if you’re dragging an item from the allowed set into your toolbar (it gets moved), if you are programmatically inserting your item, you may find yourself with many of them.

Suddenly, you have too many ponies.
Unfortunately, allowsDuplicatesInToolbar is read-only, and there is no setter for this, so if you want to ensure that you end up with _exactly one pony_ you either need to subclass NSToolbarItem or check for existing ponies in the toolbar in the inserting function. (A custom ToolbarItem that configures itself according to the settings it is given would make for neater code.)
4) So far, we’ve covered both storyboard toolbars and ones set up in code, but always in the Icon style.
Using custom controls
Technically, toolbar items can contain any control, though Apple’s Human Interface Guidelines have strict ideas on what can and cannot be included in a toolbar: specific system controls that are optimized for use in the toolbar. The HIG contain a list – basically push buttons, segmented controls, popup and pulldown menus, as well as search fields. There is no specific style recommended for text fields (can you imagine a webbrowser that does not allow you to enter a URL?) and I’m frequently seeing colorWells as well.
I have no idea whether their use would cause trouble with the AppStore, but since these are Human Interface _Guidelines_, I should hope not. (Consistency is great, but not all things can be done with the same small set of controls.)
4a) Create a CustomControlWindowController, and drag another windowController Scene into the storyboard, make it the initial controller. Set the viewController’s class to ViewController and connect the resultLabel.
Drag a toolbar into the window in the storyboard.
Delete the colors, fonts and print items from the allowed items – I recommend using the document outline, as Xcode 9.2 crashed on me when I dragged it out of the allowed items (it remained in the default items; this was not good). Let’s start by dragging a texture rounded button into the allowed items area.

Since you can give the button itself a label, neither the ToolbarItem Label nor PaletteLabel are necessary; set the identifier to TextureButton
Add an action to the ViewController
@IBAction func buttonClicked(_ sender: NSButton){
resultLabel.stringValue = "Button was clicked"
}
and connect the button to the FirstResponder’s buttonClicked method by dragging from the document outline or the toolbar setup area. (Not the button as displayed in the Window in IB. This is not responsive).
Note that the sender now is the control, not an NSToolbarItem, and that you have a choice about customization: either the button acquires a label or you disallow customization; otherwise the ‘text only’ version of your toolbar will look like this:

4b) You can use segmented controls, in textured/rounded or separated styles. What you cannot do in a storyboard is to give each segment a label underneath – there is one item for the whole segmented control.
The trick here lies in creating the toolbar in code and configuring the toolbar items in the itemsForItemIdentifier method, using NSToolbarItemGroup.
This is described in this article by Christian Tietze, so you might as well read it there. You create a NSToolbarItemGroup, add subitems for the individual segments, then add a view (the segmented control itself). The link to the follow-up post which deals with validation is broken, so I looked for it so you don’t have to: it’s worth considering if you want to go that route.
5) All that’s left is to look at the toolbar style of NSTabView, which has some interesting quirks of its own.
NSTabView Toolbar
5a) Drag one last windowController into the app, make it the initial controller, and create a TabWindowController as its class. Delete its viewController and drag a TabViewController into the storyboard; make that the windowController’s contentView. Give the tabcontroller’s subviews labels, so you can distinguish them, select the tabViewItems and add an icon to each and change the labels in IB. (I’m just using ‘One’ and ‘Two’ here.) If you double-click the tabs, the name will change in the tab and in the tabviewController’s ‘tabs’ list (attribute inspector), giving them labels overrides that, and changes the name in the outlineView. (Changing the name in the outlineView, however, has no effect anywhere else.)
It is worth noting that the ‘identifier’ in IB suggests that you enter a string; but since its type is Any? you can set any object, including enum cases, in code.
Change the tabViewController’s (not the tabView’s) style to ‘Toolbar’.
Build and Run.

5b) So far, so marvellous. This works like a dream if the TabViewController is the window’s contentController. In the app I’m working on, I’m using TabViewControllers for flow control, and the one that I wanted to show tabs was nested inside another tabViewController, at which point things went terribly wrong.
Let’s imitate that, because it tells us a lot about how the ‘toolbar’ style works. Create a ToolbarTabViewController for this scene.
Drag another TabViewController into the storyboard, set *it* as the window’s contentController, delete one of the contentViews and make the ToolbarTabViewController a tab bar item. Label them so you now have ‘Plain’ and ‘ToolbarTabView’.
Build and Run.
Switch to the ToolbarTabview, switch _its_ tabs, switch back to the Plain view, switch back to the ToolbarTabView, and… wait a moment?

When I first encountered this, it had me completely stumped. After playing with toolbars for a while, I reconised it as a toolbar with no, or no visible items (se 4a), but what exactly is going on here needs some unravelling.
5c) The first thing to understand is that if you have a TabViewController set to ‘Toolbar’ mode, it will be set as the delegate of the window’s toolbar and its toolbar will be the window’s toolbar. You can see that in action by dragging a toolbar into the window. In ‘Plain’ mode, you get the standard toolbar. Switch to ToolbarTabView, and you get its custom toolbar with the two tabs; switch to plain and there is no toolbar, no option to show or customize it. There is no toolbar. Switch back to ToolbarTabView, and you get a toolbar, which you may hide or show, but which contains no tabs.
NSTabViewController conforms to NSToolbarDelegate, so in theory, you can simply drop in the following:
override func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
print("toolbar will add \(toolbarItem.itemIdentifier)")
}
}
override func toolbarDidRemoveItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
print("toolbar removed \(toolbarItem.itemIdentifier)")
}
}
… but here we meet another quirk of NSToolbar. Right now, at this point in the app (10.12.6, Xcode 9.2), the delegate is Optional(). This is an internal class as reported in this bug in 2015. This does not seem to impede functionality.
willAddItem and didRemoveItem are called, however (as is itemForIdentifier, which is useless for our purpose since the default NSToolbarItem constructor does not use our tab icons; overriding it makes a mockery of the easy ‘a relevant, selectable toobar item gets created automatically’, though you might wish to add a search field.)
But at least we now have a diagnosis: the items are automatically added the first time the TabController becomes active, and removed when it becomes inactive… and they will never be created again.
5d) The conclusion of 5c is that we need to capture these items when they are created and add them back whenever we switch to the ToolbarTabViewController.
var defaultToolbarItems: [NSToolbarItem] = []
override func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
if defaultToolbarItems.count < tabView.numberOfTabViewItems{
defaultToolbarItems.append(toolbarItem)
}
This is the easiest way to capture items. Remember the multitude of ponies in 3d? We need to make sure that we’re not duplicating tabs every time they get added. There are more elegant solutions, but for now, this will work.
5e) The work of adding the tabs back is done in the following method:
@IBAction func addTabsBack(_ sender: Any){
if let window = view.window {
if let toolbar = window.toolbar{
for (index, item) in defaultToolbarItems.enumerated() {
toolbar.insertItem(withItemIdentifier: item.itemIdentifier, at: index)
}
}
}
}
5f) Which leaves the question of when to call this.
viewWillAppear is too early; viewDidAppear seems to work,
override func viewDidAppear() {
addTabsBack(self)
}
Set the first tabViewController to ‘plain’; build and run. There’s a noticable lag between the tab appearing and the items actually being shown, but it seems to work.
Now switch the tabViewController to ToolbarTabView. In this app, we get two items. In another app, with the same code, I got two instances of each item.

I also don’t like the lag, so I’ve created an OuterTabController to handle this. Remove the viewWillAppear method, and set the containing tabViewController to
class OuterTabController: NSTabViewController {
override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
print("Switching tabs")
if let destination = tabViewItem?.viewController as? ToolbarTabController{
destination.addTabsBack(self)
}
}
}
(willSelect is too early)
Build and run. Now exactly two tabs appear, in the correct order, every time you switch to the ToolbarTabViewController, and they work exactly as they should.
6) You cannot have more than one row of buttons in your toolbar, or add a second toolbar, but you can add an accessory view. The ‘markup toolbar’ in Preview, for instance, provides additional actions.
Go back and set the ToolbarWindowController as the Application’s Inital ViewController.
6a) Drag a new viewController into the storyboard. Add a button (recessed seems to be the best fit). Create a subclass of NSTitlebarAccessoryViewController and call it AccessoryViewController. Create an action for the button and link them up:
@IBAction func acessoryAction(_ sender: NSButton) {
print("Accessory Action Triggered")
}
You will need to provide a little padding, for your controls, but not a lot; otherwise the Toolbar will take over too much of the screen. This is the point where toolbar’s separator makes a real visual difference.
6b) In the ToolbarWindowController, create a new function:
func setUpAccessoryController(){
let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
if let accessoryController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "TitlebarController")) as? TitlebarController {
self.window?.addTitlebarAccessoryViewController(accessoryController)
}
}
and call it in windowDidLoad()
override func windowDidLoad() {
super.windowDidLoad()
window?.toolbar?.delegate = self
setUpAccessoryController()
}
Build and run.

If you hide the toolbar, the accessory view will remain; so you might need to customize this further.
As a bonus, titlebarAccessoryViewControllers is an array of accessoryViewControllers, so you can have more than one, and add or remove them at will.
And more…
Usage Example
Large numbers of modern Cocoa apps. Toolbars – particularly the slightly less intrusive version with custom controls – are extremely common everywhere.
Alternatives
For tab views, other forms of tabs. Elsewhere, you can use palettes – an interface feature that has become less and less common – and inspectors. Personally, as someone who works on a 13″ or 15″ screen, I like toolbars for the most common controls and collapsible palettes (rather than an inspector in the right-hand column of a splitview); but that’s up to everybody’s taste and the individual app. Accessory views (like the Markup Toolbar in Preview) can give quick access to additional controls while keeping the appearance of the app unified and uncluttered.
Extensions
If I wanted to use custom toolbarItems, I’d definitely subclass NSToolbarItem and add a settings variable, which takes an ToolbarTemplate and configures the item on didSet. itemForItemIdentifier is unwieldy and involves too much boilerplate.
Jan 27 2018
Toolbar Tutorial
Toolbars have been an integral part of macOS since the beginning. They provide the opportunity to display frequently used controls to the user. Where older macOS applications might have presented a pallet with certain options, modern macOS apps tend to use toolbars. While Apple provides an extensive guide for macOS toolbars, it was last updated in February 2009.
In this tutorial, we will build two types of toolbar (an icon toolbar and one using custom controls), experiment with hooking up toolbar items and their actions, and as a bonus item, look into the toolbar mode of NSTabView.
tl; dr: NSToolbar does show its age; it does not play too well with storyboards, and after writing this post, I am really hoping for a major update soon.
Basic toolbar in in storyboard0) This creates the most basic toolbar.
Create a new macOS application. Drag a toolbar into the WindowController scene. Build and run.
This is your basic toolbar. The Colours and Fonts items are fully functional, the Print button is not.
0a) In the ‘View’ menu, you will find two relevant items: Show/Hide Toolbar and Customize Toolbar.
Customize gives you the following sheet:

In other words, it shows the toolbar as it is right now, the items that are allowed, and gives the user the opportunity to change settings – icons and text, icon only, text only, plus the option of arranging the order of items.
0b) The allowed items correspond to the setup of the toolbar in Interface Builder:

When you search the ObjectLibrary, you will see that the only other available item is an ‘Image Toolbar Item’
This is radar-worthy: the NSToolbarItem.Identifier struct contains not only these, but two deprecated items (fair enough), as well as two relatively new items:
static let toggleSidebar
//The standard toolbar item identifier for a sidebar. It sends toggleSidebar: to firstResponder. (macOS 10.11+)static let cloudSharing
//(macOS 10.12+); no discussion availablewhich are not exposed in IB and thus cannot be created easily.
1) Let’s create a custom toolbar with custom ImageToolbar Items.
1a) Add three images to your application’s asset catalogue. (Mine are a butterfly, fish, and lizard). You can add toolbar items in two ways: either double-click the toolbar and drag the item to the ‘allowedItems’ area, or drag it into the toolbar in the document outline (see image above). By default, they appear as ‘toolbar item’ in the toolbar.
Since we won’t work with them, remove the Fonts and Print items (but keep the colours for now).
In the attributes inspector, add your icons to the three new elements.
Rearrange the default toolbar so that your custom items appear in it. Here is the first quirk of toolbars: if you name them in the document outline, both label and palette label will remain as ‘toolbar item’; you need to rename them in the Attributes Inspector. Here’s the second quirk: The ‘allowedItems’ listing uses the Palette Label (here: Butterfly), while the default items (and the toolbar shown to the user) uses the Label (‘Fly’).
Right now, I don’t see much reason to use different labels, but if you are using this style of control, you should set both. (When using custom controls, the ability to show a label in the customisation area becomes more interesting.)
1b) Right now, our custom items are greyed out since they have no actions associated with them. The easiest way to change that is to create IBActions in a new WindowController subclass and connect them directly. Create a ToolbarWindowController subclass of NSWindowController, assign it to your window’s class in the storyboard, and drag from each toolbar item to the windowController.
@IBAction func fly(_ sender: NSToolbarItem) {
print("Fly away")
}
@IBAction func swim(_ sender: NSToolbarItem) {
print("Swimming in circles")
}
@IBAction func bask(_ sender: NSToolbarItem) {
print("I'm enjoying my rock")
}
Build and Run.
1c) So far, so good. Our toolbar items are connected to actions. The bad news is that these actions live in the WindowController. Back in the days of macOS 10.9 and earlier, WindowControllers played a much bigger role in app design than they do now; but in modern apps – particularly if, like I do, you use storyboards – ViewControllers tend to be where code lives, which makes for more modular, more flexible app design.
The alternative to a direct connection is to declare these actions in the ViewController and connect them via first responder. Copy the methods above, and paste them into the ViewController subclass.
Drag a label into the ViewController scene, widen it to sentence length, create an outlet for it:
@IBOutlet weak var resultLabel: NSTextField!
and change the ‘print’ in the statements above toresultLabel.stringValue =
Now disconnect the toolbar buttons from the actions in the ToolbarWindowController and drag to the FirstResponder and find the function names (fly/swim/bask) and connect to those.
Comment out the methods in the WindowController to make this work, otherwise *they* will be called.
Build and Run. The label text will change.
So that’s a basic working toolbar. Next up: toolbar item properties.
Labels, images, selectability, and more.1d) Let’s take a look at the standard toolbarItem attributes.

ImageName, Label, and Palette Label we have dealt with. Identifier we will leave alone for now, since this is a predetermined toolbar (we create the items in IB). Tag is an oddity: by default, all NSToolbarItem tags are -1. This includes the space/flexible space. The impression this gives is that – unlike other controls – it is not common to refer to toolbarItems by tag. (Expectation was that the non-selectable ones – space/flexiSpace – are -1, and the rest would be 0 by default.)
Auto-generated Identifiers are long and cumbersome Strings, so I recommend entering a value (Lizard or BD921647-5EA6-4762-9FFF-551666D60CEC ? You decide.)
This leaves three items that may be of interest: Priority, Autovalidates, and Selectable.
Autovalidates is selected by default. Comment out ‘Autovalidates’ for the butterfly button and comment out the ‘Fly’ action in the viewController. Build and run. The button appears enabled, shows the animation for being clicked, but doesn’t do anything. Enable ‘Autovalidates’ and it will be greyed out and unresponsive.
(Don’t forget to uncomment the fly action again.).
This is a good setting, a very useful one; under most circumstances you will want it set to true.
Now set ‘selectable’ for each of the three buttons. The button that was selected last now shows a slight grey highlight. When, like here, you are using a group of toolbar items to switch modes, that, too, is a useful setting. When, on the other hand, the items are one-shot actions, you want to uncheck selectable.

If you are using selectable toolbar items, you can use NSToolbar’s func selectedItemIdentifier() to find out which item is currently selected. (this returns an optional NSToolbarItem.Identifier)
This leaves the trickiest setting, priority. It’s more of an enigma. The documentation only has an entry for ‘visibility priority’ which says
The receiver’s visibility priority and contains the discussion Possible values are described in Item Priority. (Spoiler: there is no entry in NSToolbarItem for ‘ItemPriority’.)
NSToolbarItem.VisibilityPriority is a struct, and gets weirder still:
it has two initialisers, init(Int) and init(rawValue: Int). They seem to do the same thing, and the first may be an artefact of earlier Swift syntax; for consistency’s sake I would recommend using the second. It also has four type properties:
– high
– low
– standard
– user
and the discussion makes it clear what this is about: the likelyhood of an item being pushed to the overflow menu when the window is too narrow to acommodate all of the items.
I set the priorities of mine to fly: 0, swim: 5, bask: 20. When narrowing the window in the setup pictured above, they were pushed out in the following order: Fly, Colors, Swim. Colors always beats fly, so: items of equal priority get booted from trailing to leading (have not tested with right-to-left language, but I’m assuming this), items with other priorities get booted in order of priorities.
I am still somewhat unclear where ‘user’ priority comes in here, or whether that simply IS a reflection of ‘user put these items to the left side of the window; we assume they want to see them’.
When you access the priorities, they will be displayed as VisibilityPriority(rawValue: 5) etc, so the low/standard/high seems to be an internal calculation.
1e) The toolbar itself exposes no interesting settings in IB, but there are a few properties that are worth taking note of:
var allowsExtensionItems: Bool //false by default. If set to true, it allows the toolbar to add ActionExtensions dynamically, the most common one being the ‘Share’ extension.
var identifier: NSToolbar.Identifier
//in case you want more than one toolbar in your application – if you have a photo browsing and photo editing mode, for instance, you might want two context-specific toolbars. This is how you get to know them.var items: [NSToolbarItem] var visibleItems: [NSToolbarItem]?
(these are read-only)
var configuration: [String: Any]
provides another quirk; mine printed out asOptional([“TB Icon Size Mode”: 1, “TB Size Mode”: 1, “TB Display Mode”: 1, “TB Is Shown”: 1]) even though the documentation states Contains displayMode, isVisible, and a list of the item identifiers currently in the toolbar. I’m suprised that icon size mode and size mode are separate entries, but I’m not seeing the ‘list of item identifiers’. (This is a read-only property with a
func setConfiguration([String : Any])
method to complement it.) Since NSToolbar has an autosavesConfiguration property, I would have expected the itemIdentifiers to be included. With ‘autosaves configuration’ the user-determined toolbar is preserved between app launches, even in debug mode, so there may be something else going on here, and indeed: if you customize the toolbar, items and visible items get added to the configuration dictionary, but only then.NSToolbar has, as so many items in the Cocoa frameworks do, a delegate property.
NSToolbarDelegate: not working as expected; bug report pending2) We have toolbar items; they do stuff. This is enough to get you up and running for draft purposes, but you can do more. Let’s look at the delegate first.
weak var delegate: NSToolbarDelegate? { get set }
The documentation says: Every toolbar must have a delegate, which must implement the required delegate methods.
Spoiler: In Swift, this is an optional delegate… and the toolbar has been working just fine without one in our storyboard.
Bug Warning: Read this warning before following the tutorial
To make the tutorial easier to follow, and in the hope that different configurations do not suffer from the same bug, I shall pretend that if you use the code I write below, your app will work as advertised. Under 10.12.6/Xcode 9.2, it did not. Whether delegate methods are called or not appears to be entirely random (I am filing a radar for this); it does not reflect on the code. This does, of course, make it completely impossible to use a storyboard-generated toolbar in production if you want to add new items in code (if you don’t, and you can set up all items you wish to use in IB, you’re fine: toolbars are great).
If the code you find below does not work, I am sorry, but you need to yell at Apple, not at me.
(The next two screenshots are *exactly the same code* running on different iterations. All I’ve done was press the ‘run’ button several times util I could get my screenshot.)
I apologise for the lousy tutorial experience – maybe it will work – but I hope that this will get fixed before you read the tutorial; once I am satisfied that it is fixed for good, I will remove the warning. For now, I shall pretend that the code works every time; it most certainly works for toolbars created in code, but there are some additional quirks regarding storyboards that I found well worth documenting; and ultimately, my goal is to work with storyboards.
For the purposes of this tutorial only, I will use the ToolbarWindowController since it’s much easier to set up; in a real app I would either use a dedicated ToolbarDelegate class (if it has lots of work to do/I use several view controllers with the same toolbar) or my root viewController (if there’s only one.)
2) Make the ToolbarWindowController conform to the NSToolbarDelegate protocol, and set it as the toolbar’s delegate:
override func windowDidLoad() {
super.windowDidLoad()
window?.toolbar?.delegate = self
//see warning above: under 10.12.6/Xcode 9.2, the delegate is always set correctly, but the delegate methods are only called some of the time.
}
If you’re wondering what the methods are that the delegate has to implement, you’re in good company. The NSToolbarDelegate documentation identifies as: A set of optional methods implemented by toolbar delegates to configure the toolbar and respond to changes.
2a) The following has a curious relationship with the toolbar we’ve set up in storyboards: it’s cumulative. After implementing this, my expectation was that I would get only a single item.
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining allowed items")
return[NSToolbarItem.Identifier.showFonts] }
Instead, my customize Toolbar area now looks like this:
This method gets called only when you choose the ‘customize Toolbar’ menu item.
Adding items that you have already added in InterfaceBuilder – as long as they are part of the standard set – results in only one copy of your item.
2b) Comment out the method above, and try the following for size:
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining default items")
return[NSToolbarItem.Identifier.showFonts] }
This, too, is cumulative: now the Fonts item gets added to the toolbar on startup; the items set in IB (respectively set in earlier customisation and autosaved in the configuration) are still there.
2c) Maybe unsurprisingly,
func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining selectable items")
return[NSToolbarItem.Identifier.showFonts] }
is ALSO cumulative: even though I have commented out 2b); the Fonts item turns up in my toolbar, and it’s selectable. It cannot, however, be customized, and if the user removes it from the toolbar and saves that configuration, it will be lost forever. This looks like an interesting source of bugs.
2d) Leave the function above, and add
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem{
print("Toolbar will add item \(toolbarItem.itemIdentifier)")
}
}
This gets called for every toolbar item.
func toolbarDidRemoveItem(_ notification: Notification) works in a similar fashion; right now it gets called only when the user customises the toolbar.
Usually, notifications are more finely grained and allow for validation: willAddItem/didAddItem and willRemoveItem/DidRemoveItem as well as having a shouldAdd/shouldRemove option. willAdd/didRemove feels a little sparse.
2e) Remove the font item in the customize Toolbar dialog, comment out 2c) and run to ensure we no longer have a Font item.
There is one last delegate method that’s worth looking at:
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
print("item for identifier called")
if itemIdentifier == NSToolbarItem.Identifier.showFonts {
let toolbarItem = NSToolbarItem(itemIdentifier: .showFonts)
toolbarItem.visibilityPriority = NSToolbarItem.VisibilityPriority(rawValue: 40)
return toolbarItem
} else {
return nil
}
}
This is the bit where you read which identifier is being requested and you return an appropriate item. It is also the only method that allows you to customise toolbar items.
This, too, turns out to be cumulative: your showFonts item will have the settings you requested, but the other items are still being created with their default storyboard settings. My expectation, in line with other delegate methods in other protocols was that this particular (nonsensical) implementation would mean that only the showFonts item gets created, but this is not the case.
The problem is that so far, even when the other delegate methods work, it has not been called once with the storyboard setup.
Calling
self.window?.toolbar?.insertItem(withItemIdentifier: .showFonts, at: 0)
in the ToolbarWindowController’s ‘windowDidLoad’ method inserts the item, but also does not fire the delegate method.This means that there is no chance to create new NSToolbarItems and customize them (giving them icons, titles, priorities etc) other than in willAddItem. To make this work you need to enable toolbarAllowedItemIdentifiers (2a above) as well as customising toolbarWillAddItem
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem{
if toolbarItem.itemIdentifier == .showFonts{
toolbarItem.visibilityPriority = NSToolbarItem.VisibilityPriority(rawValue: 40)
}
print("Toolbar will add item \(toolbarItem.itemIdentifier)")
}
}
2d) What if you want a pony? Or rather, what if you want to add an NSToolbarItem that is not part of the set available in the Cocoa framework? What if you want to create a NSToolbarItem with the identifier “Pony”?
In this version, the version that’s working with storyboards, you seem to be out of luck.
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining allowed items")
return[NSToolbarItem.Identifier.showFonts, NSToolbarItem.Identifier(rawValue: "Pony")] }
plus
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem{
if toolbarItem.itemIdentifier == .showFonts{
toolbarItem.visibilityPriority = NSToolbarItem.VisibilityPriority(rawValue: 40)
}
if toolbarItem.itemIdentifier.rawValue == "Pony"{
toolbarItem.image = #imageLiteral(resourceName: "pony")
print("I want a pony")
}
print("Toolbar will add item \(toolbarItem.itemIdentifier)")
}
}
behaves oddly: You will find ‘I want a pony’ in the console, but no new items in the allowed items area of toolbar customisation. You can add it in
windowDidLoad with
self.window?.toolbar?.insertItem(withItemIdentifier: NSToolbarItem.Identifier(rawValue: "Pony"), at: 0)
which gives you the item, but it _still_ does not appear in the allowedItems area (or the default area if you’re using 2b as well). If
itemWithIdentifier
is called (it happened a couple of times for me), this works perfectly fine.And that, as far as I’m concerned, seems to be the end of the line. If you want to set up your toolbar using storyboards, creating and adding new items in code appears to be impossible at this point in time. (Theoretically it should work. It’s just not production-ready right now.)
Creating a toolbar in code3) I think the storyboard option is wonderful – it’s easy to set up, very easy to control your items, and for most purposes, will work marvellously well. But if you really want a pony – a way of adding NSToolbarItems dynamically – right now, a toolbar in code appears to be the only way.
3a) Create a new WindowController, call it ‘CodeWindowController’ and set it as the initial windowController of your app. Create an NSWindowController subclass called CodeWindowController and set it to the windowController’s class. Make it conform to NSToolbarDelegate and set it as the toolbar’s delegate in windowDidLoad.
Set the viewController’s class to ViewController, add a label, and connect it to the resultLabel outlet.
We are going to copy much of the code from the ToolbarWindowController, but first, we need to gather the information we need; and the best way to do that is to create a quick struct (you can use a dictionary, but that’s very Objective-C: I like Swift’s strong typing.)
(I will be using the same string as the item’s label, palleteLabel and identifier, so I only need one here)
struct ToolbarItemTemplate {
var name: String = "Pony"
var image: NSImage? = #imageLiteral(resourceName: "pony")
var action: Selector = #selector(CodeWindowController.squee)
}
3b) Set up the CodeWindowController. I’m reproducing all of it below, comments inline.
class CodeWindowController: NSWindowController, NSToolbarDelegate {
var toolbar: NSToolbar?
var toolbarTemplates: [ToolbarItemTemplate] = [ToolbarItemTemplate()]
override func windowDidLoad() {
super.windowDidLoad()
setUpToolbar()
}
func setUpToolbar(){
toolbar = NSToolbar(identifier: NSToolbar.Identifier(rawValue: "CodedToolbar"))
toolbar?.allowsUserCustomization = true
toolbar?.delegate = self
self.window?.toolbar = toolbar
}
If you set up a toolbar in code, the next four functions are mandatory; your app will not function without them.
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
print("Delegate determining allowed items")
var allowedIdentifiers: [NSToolbarItem.Identifier] = [] for item in toolbarTemplates {
let id = NSToolbarItem.Identifier(rawValue: item.name)
allowedIdentifiers.append(id)
}
allowedIdentifiers.append(NSToolbarItem.Identifier.space)
allowedIdentifiers.append(NSToolbarItem.Identifier.showColors)
return allowedIdentifiers
}
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return toolbarAllowedItemIdentifiers(_:toolbar)
}
func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [] }
In other words, default and allowed identifiers are identical in this example, and no items are selectable; you can customize at will.
The following function comes with the caveat that under macOS 10.12, it works cumulatively. You can, right now, remove the ‘switch itemIdentifier’ part, which theoretically should fall back to ‘return nil’, but if you remove the handling of .showColors from this function, you will still get a fully-formed NSToolbar item. This does not come as a great surprise – after all, toolbar items were created in the storyboard verion without calling this function – but it’s also the sort of thing that could break with any future macOS update, so just because it works doesn’t mean that your code is correct.
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
let toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
switch itemIdentifier{
//handle any cases you have added to defaultItemIdentifiers
case .space, .showColors: return toolbarItem
default:
let wantedItem = itemIdentifier.rawValue
let templateArray = toolbarTemplates.filter{$0.name == wantedItem }
if !templateArray.isEmpty{
let template = templateArray[0] toolbarItem.image = template.image
toolbarItem.label = template.name
toolbarItem.paletteLabel = template.name
toolbarItem.action = template.action
return toolbarItem
}
}
//this should never be called if you're handling all identifiers correctly above, but it's structurally necessary
return nil
}
@objc func squee(){
print("I has a pony")
}
}
3c) Build and Run. Click on the ‘Pony’ button. Squee.
3d) It’s worth pointing out that by default, NSToolbarItem’s allowsDuplicatesInToolbar is ‘true’. And while Cocoa handles things smartly if you’re dragging an item from the allowed set into your toolbar (it gets moved), if you are programmatically inserting your item, you may find yourself with many of them.
Suddenly, you have too many ponies.
Unfortunately, allowsDuplicatesInToolbar is read-only, and there is no setter for this, so if you want to ensure that you end up with _exactly one pony_ you either need to subclass NSToolbarItem or check for existing ponies in the toolbar in the inserting function. (A custom ToolbarItem that configures itself according to the settings it is given would make for neater code.)
4) So far, we’ve covered both storyboard toolbars and ones set up in code, but always in the Icon style.
Using custom controlsTechnically, toolbar items can contain any control, though Apple’s Human Interface Guidelines have strict ideas on what can and cannot be included in a toolbar: specific system controls that are optimized for use in the toolbar. The HIG contain a list – basically push buttons, segmented controls, popup and pulldown menus, as well as search fields. There is no specific style recommended for text fields (can you imagine a webbrowser that does not allow you to enter a URL?) and I’m frequently seeing colorWells as well.
I have no idea whether their use would cause trouble with the AppStore, but since these are Human Interface _Guidelines_, I should hope not. (Consistency is great, but not all things can be done with the same small set of controls.)
4a) Create a CustomControlWindowController, and drag another windowController Scene into the storyboard, make it the initial controller. Set the viewController’s class to ViewController and connect the resultLabel.
Drag a toolbar into the window in the storyboard.
Delete the colors, fonts and print items from the allowed items – I recommend using the document outline, as Xcode 9.2 crashed on me when I dragged it out of the allowed items (it remained in the default items; this was not good). Let’s start by dragging a texture rounded button into the allowed items area.
Since you can give the button itself a label, neither the ToolbarItem Label nor PaletteLabel are necessary; set the identifier to TextureButton
Add an action to the ViewController
@IBAction func buttonClicked(_ sender: NSButton){
resultLabel.stringValue = "Button was clicked"
}
and connect the button to the FirstResponder’s buttonClicked method by dragging from the document outline or the toolbar setup area. (Not the button as displayed in the Window in IB. This is not responsive).
Note that the sender now is the control, not an NSToolbarItem, and that you have a choice about customization: either the button acquires a label or you disallow customization; otherwise the ‘text only’ version of your toolbar will look like this:

4b) You can use segmented controls, in textured/rounded or separated styles. What you cannot do in a storyboard is to give each segment a label underneath – there is one item for the whole segmented control.
The trick here lies in creating the toolbar in code and configuring the toolbar items in the itemsForItemIdentifier method, using NSToolbarItemGroup.
This is described in this article by Christian Tietze, so you might as well read it there. You create a NSToolbarItemGroup, add subitems for the individual segments, then add a view (the segmented control itself). The link to the follow-up post which deals with validation is broken, so I looked for it so you don’t have to: it’s worth considering if you want to go that route.
5a) Drag one last windowController into the app, make it the initial controller, and create a TabWindowController as its class. Delete its viewController and drag a TabViewController into the storyboard; make that the windowController’s contentView. Give the tabcontroller’s subviews labels, so you can distinguish them, select the tabViewItems and add an icon to each and change the labels in IB. (I’m just using ‘One’ and ‘Two’ here.) If you double-click the tabs, the name will change in the tab and in the tabviewController’s ‘tabs’ list (attribute inspector), giving them labels overrides that, and changes the name in the outlineView. (Changing the name in the outlineView, however, has no effect anywhere else.)
It is worth noting that the ‘identifier’ in IB suggests that you enter a string; but since its type is Any? you can set any object, including enum cases, in code.
Change the tabViewController’s (not the tabView’s) style to ‘Toolbar’.
Build and Run.
5b) So far, so marvellous. This works like a dream if the TabViewController is the window’s contentController. In the app I’m working on, I’m using TabViewControllers for flow control, and the one that I wanted to show tabs was nested inside another tabViewController, at which point things went terribly wrong.
Let’s imitate that, because it tells us a lot about how the ‘toolbar’ style works. Create a ToolbarTabViewController for this scene.
Drag another TabViewController into the storyboard, set *it* as the window’s contentController, delete one of the contentViews and make the ToolbarTabViewController a tab bar item. Label them so you now have ‘Plain’ and ‘ToolbarTabView’.
Build and Run.
Switch to the ToolbarTabview, switch _its_ tabs, switch back to the Plain view, switch back to the ToolbarTabView, and… wait a moment?
When I first encountered this, it had me completely stumped. After playing with toolbars for a while, I reconised it as a toolbar with no, or no visible items (se 4a), but what exactly is going on here needs some unravelling.
5c) The first thing to understand is that if you have a TabViewController set to ‘Toolbar’ mode, it will be set as the delegate of the window’s toolbar and its toolbar will be the window’s toolbar. You can see that in action by dragging a toolbar into the window. In ‘Plain’ mode, you get the standard toolbar. Switch to ToolbarTabView, and you get its custom toolbar with the two tabs; switch to plain and there is no toolbar, no option to show or customize it. There is no toolbar. Switch back to ToolbarTabView, and you get a toolbar, which you may hide or show, but which contains no tabs.
NSTabViewController conforms to NSToolbarDelegate, so in theory, you can simply drop in the following:
override func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
print("toolbar will add \(toolbarItem.itemIdentifier)")
}
}
override func toolbarDidRemoveItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
print("toolbar removed \(toolbarItem.itemIdentifier)")
}
}
… but here we meet another quirk of NSToolbar. Right now, at this point in the app (10.12.6, Xcode 9.2), the delegate is Optional(). This is an internal class as reported in this bug in 2015. This does not seem to impede functionality.
willAddItem and didRemoveItem are called, however (as is itemForIdentifier, which is useless for our purpose since the default NSToolbarItem constructor does not use our tab icons; overriding it makes a mockery of the easy ‘a relevant, selectable toobar item gets created automatically’, though you might wish to add a search field.)
But at least we now have a diagnosis: the items are automatically added the first time the TabController becomes active, and removed when it becomes inactive… and they will never be created again.
5d) The conclusion of 5c is that we need to capture these items when they are created and add them back whenever we switch to the ToolbarTabViewController.
var defaultToolbarItems: [NSToolbarItem] = []
override func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
if defaultToolbarItems.count < tabView.numberOfTabViewItems{
defaultToolbarItems.append(toolbarItem)
}
This is the easiest way to capture items. Remember the multitude of ponies in 3d? We need to make sure that we’re not duplicating tabs every time they get added. There are more elegant solutions, but for now, this will work.
5e) The work of adding the tabs back is done in the following method:
@IBAction func addTabsBack(_ sender: Any){
if let window = view.window {
if let toolbar = window.toolbar{
for (index, item) in defaultToolbarItems.enumerated() {
toolbar.insertItem(withItemIdentifier: item.itemIdentifier, at: index)
}
}
}
}
5f) Which leaves the question of when to call this.
viewWillAppear is too early; viewDidAppear seems to work,
override func viewDidAppear() {
addTabsBack(self)
}
Set the first tabViewController to ‘plain’; build and run. There’s a noticable lag between the tab appearing and the items actually being shown, but it seems to work.
Now switch the tabViewController to ToolbarTabView. In this app, we get two items. In another app, with the same code, I got two instances of each item.
I also don’t like the lag, so I’ve created an OuterTabController to handle this. Remove the viewWillAppear method, and set the containing tabViewController to
class OuterTabController: NSTabViewController {
override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
print("Switching tabs")
if let destination = tabViewItem?.viewController as? ToolbarTabController{
destination.addTabsBack(self)
}
}
}
(willSelect is too early)
Build and run. Now exactly two tabs appear, in the correct order, every time you switch to the ToolbarTabViewController, and they work exactly as they should.
6) You cannot have more than one row of buttons in your toolbar, or add a second toolbar, but you can add an accessory view. The ‘markup toolbar’ in Preview, for instance, provides additional actions.
Go back and set the ToolbarWindowController as the Application’s Inital ViewController.
6a) Drag a new viewController into the storyboard. Add a button (recessed seems to be the best fit). Create a subclass of NSTitlebarAccessoryViewController and call it AccessoryViewController. Create an action for the button and link them up:
@IBAction func acessoryAction(_ sender: NSButton) {
print("Accessory Action Triggered")
}
You will need to provide a little padding, for your controls, but not a lot; otherwise the Toolbar will take over too much of the screen. This is the point where toolbar’s separator makes a real visual difference.
6b) In the ToolbarWindowController, create a new function:
func setUpAccessoryController(){
let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
if let accessoryController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "TitlebarController")) as? TitlebarController {
self.window?.addTitlebarAccessoryViewController(accessoryController)
}
}
and call it in windowDidLoad()
override func windowDidLoad() {
super.windowDidLoad()
window?.toolbar?.delegate = self
setUpAccessoryController()
}
Build and run.

If you hide the toolbar, the accessory view will remain; so you might need to customize this further.
As a bonus, titlebarAccessoryViewControllers is an array of accessoryViewControllers, so you can have more than one, and add or remove them at will.
And more…
Usage Example
Large numbers of modern Cocoa apps. Toolbars – particularly the slightly less intrusive version with custom controls – are extremely common everywhere.
Alternatives
For tab views, other forms of tabs. Elsewhere, you can use palettes – an interface feature that has become less and less common – and inspectors. Personally, as someone who works on a 13″ or 15″ screen, I like toolbars for the most common controls and collapsible palettes (rather than an inspector in the right-hand column of a splitview); but that’s up to everybody’s taste and the individual app. Accessory views (like the Markup Toolbar in Preview) can give quick access to additional controls while keeping the appearance of the app unified and uncluttered.
Extensions
If I wanted to use custom toolbarItems, I’d definitely subclass NSToolbarItem and add a settings variable, which takes an ToolbarTemplate and configures the item on didSet. itemForItemIdentifier is unwieldy and involves too much boilerplate.
By Extelligent Cocoa • Bug Reports, Interface, Tutorials • • Tags: NSTitlebarAccessoryViewController, NSToolbar, NSToolbarItem