oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Creating Toolbars for Mac OS X
Pages: 1, 2, 3, 4

The Add and Remove Items

In configuring these two items, we set five properties that make up a minimum useable control. A toolbar item has two labels -- one for when the item is displayed in the toolbar, and the other for when the item is shown in the customization palette.

The methods -setLabel: and -setPaletteLabel: do this, so weíll call these two methods as part of our configuration. Additionally, we want to give our toolbar item an image. You will see that I have included two images for use in this application in the project folder -- Add.tiff and Remove.tiff. Toolbar item images are set with the method -setImage:.

Finally, we want to set the target of the toolbar item and the action to be invoked in the target. This is done with -setTarget: and -setAction:, respectively. Hereís the code to do these five things for AddItem and RemoveItem:

if ( [itemIdentifier isEqualToString:@"AddItem"] ) {
	[item setLabel:@"Add Record"];
	[item setPaletteLabel:[item label]];
	[item setImage:[NSImage imageNamed:@"Add"]];
	[item setTarget:self];
	[item setAction:@selector(addRecord:)];
    } else if ( [itemIdentifier isEqualToString:@"RemoveItem"] ) {
	[item setLabel:@"Remove Record"];
	[item setPaletteLabel:[item label]];
	[item setImage:[NSImage imageNamed:@"Remove"]];
	[item setTarget:self];
	[item setAction:@selector(deleteRecord:)];
    } else if ( [itemIdentifier isEqualToString:@"SearchItem"] ) {
	// Configuration code for "SearchItem"

There are several things to note here. One, notice that we set the palette label to be the same as the toolbar label. If you wanted, you could give the item a more descriptive palette label, since space is not as much of a premium there as in the toolbar. We used the -label method to obtain the label string rather than duplicating the static string. Not such a big deal, but just one less thing to change if we wanted to change the label.

Second, notice how we created an NSImage instance from the image file to pass along in the -setImage: message to the item. NSImageís +imageNamed: is extremely convenient for this type of work. You donít have to pass the file path or extension -- just the name -- and +imageNamed: will look for the image in the applicationís bundle.

Finally, note that the actions of these two toolbar items are set to the same actions as the Add and Remove buttons in the interface. For the complete rundown on everything you can fiddle with in a toolbar item, check out the NSToolbarItem reference.

The Search Item

Now we get to the Search toolbar item. Weíre going to use Interface Builder to do some of the work here. To prepare for that add the following outlet declaration to Controller.h:

IBOutlet id searchItemView;

Then go to Interface Builder and read in the Controller.h file to update the NSController class interface, making the outlet available to us. What we want to do is create a control in IB that will be encapsulated in a toolbar item. Since there is no toolbar or anything of that sort to put the text field into, we build our toolbar item in a standalone instance of NSView. Weíll add an NSView instance to our nib, like we learned how to do last time, and then place a text field within this view.

We also need to set the size of the view to match the size of the text field. We do this to make the toolbar item as compact as possible. A word of caution on this -- donít manually size the view to fit the text field. Rather, you should size things in the Info Panel under Size. For our text field, change the bottom/left of the frame from its current setting to x = 0, and y = 0. This will wedge the text field into the bottom left-hand corner of the view. Next, take note of the width and height of the text field frame, and in the viewís size info, change the height and width to those values.

Sizing like this becomes especially important when youíre working with buttons or other objects with shadows. We donít want to cut off the shadows of the controls, otherwise we would have some funky-looking controls. Now, to finish things off here make the connection between the outlet searchItemView and the NSView instance which contains the text field.

One last thing we need to do before closing up shop is to connect NSController to the delegate outlet of the text field. We are doing this so we can take advantage of a nifty delegate method of NSControl later on. Do this now, save your work, and return to Project Builder.

The code for configuring the search toolbar item starts out in the same way the others do by setting the labels of the item. After that, we do things differently. Rather than setting the toolbar item image, we set the itemís view. The toolbar item becomes the set of controls contained in the view object -- in our case the single text field. This makes it easy to create complex toolbar items that contain a variety of controls such as pop-up buttons or switch buttons.

After setting the toolbar itemís view, we have to tell the item how large and how small it may be sized. This is done using the -setMinSize: and -setMaxSize: methods. We will set both of these to the current size of searchItemView, which we get directly from the view.

Due to the nature of the search field, we donít need to set the target or action -- weíre going to take advantage of Controllerís status as the text fieldís delegate to work with this control. Letís finally take a look at the code that makes all of this happen (this is the third else-if statement):

} else if ( [itemIdentifier isEqualToString:@"SearchItem"] ) {
    NSRect fRect = [searchItemView frame];
    [item setLabel:@"Search Records"];
    [item setPaletteLabel:[item label]];
    [item setView:searchItemView];
    [item setMinSize:fRect.size];
    [item setMaxSize:fRect.size];

First we obtained the frame of searchItemView and stored it in fRect -- this will come in handy when we need to set the minimum and maximum size of the control. In the next two lines we set the labels as we did previously. Following that, we invoke -setView: to set the view of the toolbar item to searchItemView. In the next two lines we use -setMinSize: and -setMaxSize: to set the corresponding sizes of the item. These methods both take NSSize arguments, which are provided courtesy of fRect.size. With that weíre finally ready to compile and try out our new toolbar.

Now, what I want to do is show you one more thing relating to toolbars, and then Iíll describe how the search function is implemented.

Toolbar Item Validation

Toolbar item validation refers to the method of providing some test on the state of your application to determine whether or not a toolbar item should be enabled or disabled. For example, in our situation, the tenets of good interface design would have us enable the remove item only when one or more records in the address book are selected. In this case the number of selected rows tells us something about the state of the application, which we can compare against specific validation criteria.

This behavior is conveniently supported using the NSToolbarItemValidation protocolís -validateToolbarItem: method. For any toolbar item that has a valid target/action pair, this method is called in the target object. Since NSController (via self) is the target of the Remove item, weíll implement -validateToolbarItem: in ToolbarDelegateCategory. In ToolbarDelegateCategory.h now add the declaration for this method.

The return value of this method is a BOOL, so we have to perform some test within the method implementation that returns a BOOL. We already said that we want the Remove item to be enabled only when there are records selected, so the test here would be to check if there are any selected records in the tableview. NSTableView provides the method -numberOfSelectedRows to do this for us. If the number is greater than 0, then we validate the item by returning YES. Right here we have all we need to validate the Remove toolbar item:

- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
    if ( [theItem action] == @selector(deleteRecord:) )
	return [tableView numberOfSelectedRows] > 0;

If you have several toolbar items with the same target invoking different actions, then it is imperative that you test, in some way, that the item is the toolbar item we want to validate before passing judgment on validation. A good way to do this is by comparing the action of the item to the method we want to validate. This is logical since what we really want to do is enable or disable a behavior rather than an item.

In this case, we checked to see if the action of the item in question was the deleteRecord: method. If so then we check to see how many rows are selected. If one or more are selected, then YES is returned, otherwise NO gets sent back. And thatís how toolbar item validation works!

Pages: 1, 2, 3, 4

Next Pagearrow