NSToolbar basics

Building a Cocoa based user interface using the Interface Builder is really easy and produces great results quickly. But when I wanted to add a toolbar to a window, I had to realize, that Interface Builder does not support toolbars. So I started reading the apple developer documentation regarding the NSToolbar class and found out that it is actually easy to work with toolbars. There is just no Interface Builder support for them. So here comes a small howto about NSToolbar and its use in your applications.

Cocoa works with distinct toolbar identifiers to distinguish between multiple different toolbars in one application. The toolbars can be fully customized by the user and changes to a toolbar are synchronized to all toolbars with the same identifier. For example in an application with multiple edit document windows, all these windows would have a toolbar with the same identifier. If a user would change on toolbar, alle currently opened edit windows would synchronize their toolbars to the new changes.

When you want to add a toolbar to your application, make sure, that your controller class has a handle to the window which will hold the toolbar. So you need to define an outlet

IBOutlet NSWindow *window;

and connect this outlet in Interface Builder to the window you want to use for the toolbar. Now you need to initialize the toolbar during the application startup. The best place for this is the awakeFromNib method, because at the time this method is called, all nib-Files are already initialized and our outlets are already connected. If you want to add a toolbar from a NSDocument subclass, you need to do the initialization in windowControllerDidLoadNib:.

Let’s take a look at the toolbar initialization in the awakeFromNib method of our application controller:

- (void)awakeFromNib
    // create the toolbar object
    NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"MySampleToolbar"];
    // set initial toolbar properties
    [toolbar setAllowsUserCustomization:YES];
    [toolbar setAutosavesConfiguration:YES];
    [toolbar setDisplayMode:NSToolbarDisplayModeIconAndLabel];

    // set our controller as the toolbar delegate
    [toolbar setDelegate:self];

    // attach the toolbar to our window
    [window setToolbar:toolbar];

    // clean up
    [toolbar release];

The above code is pretty straightforward. We create a new toolbar object, set some basic attributes which enable the user to modify the toolbar contents and set the initial display mode to display both icons and text for the toolbar items. Then we register ourselves as a delegate for the toolbar. All further programming is then done in the delegate methods. After that we add the toolbar to our window, and clean up memory (don’t worry the window object retains our toolbar, so it will not be disposed right away).

When working with toolbars, we need to implement at least delegates for the following actions:

  • get a list of all allowed toolbar items
  • get a list of all default items which should be displayed in our default toolbar
  • get a NSToolbarItem object for a given identifier

Again, similar to the toolbar, the toolbar items are also identified with identifier strings. It is a good practice to define these strings as static variables:

static NSString *SaveToolbarItemIdentifier = @"My Save Toolbar Item";
static NSString *SearchToolbarItemIdentifier = @"My Search Toolbar Item";

The Cocoa framework has a couple of toolbar items predefined, for example NSToolbarSeparatorItemIdentifier or NSToolbarSpaceItemIdentifier.

The first delegate method we implement will return a list of all allowed toolbar items:

- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar {
    return [NSArray arrayWithObjects:SaveToolbarItemIdentifier, 
        NSToolbarSeparatorItemIdentifier, nil];

We simply need to return an array with the identifiers of all allowed toolbar items.

The delegate to return a list of default toolbar items looks similar:

- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar 
    return [NSArray arrayWithObjects:SaveToolbarItemIdentifier,
        SearchToolbarItemIdentifier, nil];

Our sample toolbar consists of two items, which are separated with a flexible space element which results in the first item being left aligned in the toolbar and the second item being right aligned. The order of the items returned in the array is the display order in the toolbar.

The interesting part is how the toolbar items are created. The toolbar can display two types of items. Simple icon based toolbar items and items which are based on a custom view. With custom view items you can implement search fields, or popup menus or anything else you can imagine. In our example we have a search field which is a custom view item. For this item we have created a new custom view in interface builder and placed an NSSearchField into the view. Our controller has an outlet which is connected to the custom view. The resulting delegate method looks like this:

- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
     itemForItemIdentifier:(NSString *)itemIdentifier
    NSToolbarItem *toolbarItem = nil;
    if ([itemIdentifier isEqualTo:SaveToolbarItemIdentifier]) {
        toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
        [toolbarItem setLabel:@"Save"];
        [toolbarItem setPaletteLabel:[toolbarItem label]];
        [toolbarItem setToolTip:@"Save Your Passwords"];
        [toolbarItem setImage:[NSImage imageNamed:@"save.png"]];
        [toolbarItem setTarget:self];
        [toolbarItem setAction:@selector(save:)];
    } else if ([itemIdentifier isEqualTo:SearchToolbarItemIdentifier]) {
        NSRect searchRect = [searchView frame];
        toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
        [toolbarItem setLabel:@"Search Passwords"];
        [toolbarItem setPaletteLabel:[toolbarItem label]];
        [toolbarItem setToolTip:@"Search Password Titles"];
        [toolbarItem setView:searchView];
        [toolbarItem setMinSize:searchRect.size];
        [toolbarItem setMaxSize:searchRect.size];
    return [toolbarItem autorelease];

For every toolbar item you need to the set its label, a palette label and a tooltip message. For simple toolbar items, you also need to define an icon image and an action method which will be called when the item is clicked.

The setup of custom view items is a little bit different. First we read the size of the custom view. This is used to set a fixed size for the toolbar item. We do not specify a custom action, because the NSSearchField is connected to an action method itself. But whether you need to define an action method for a custom view item, depends on your view.

That’s it for the basics of toolbar programming in Cocoa. If you want to dive deeper into this topic, I recommend to read the document Toolbar Programming Topics for Cocoa.

Leave a Reply