Cocoa for Scientists (Part X): Popping Up All Over the Place
Author: Drew McCormack
Website: http://www.maccoremac.com
Just last week, we began increasing the feature set of the Unitary conversion application to include multiple unit conversions. We added an abstract base class and some subclasses to represent a conversion in the model layer, but didn’t get around to adding any UI for choosing a particular conversion. Today, we will add that UI, in the form of a pop up button. We will connect it up to the controller using actions and outlets so that when you select a particular conversion, the labels in the interface update appropriately, and the correct conversion takes place.
Class Diagrams
Last week, there was a little feedback about design, and whether I have a big design document somewhere for Unitary. I don’t, though I do have a few ideas about where I can take the project. Unitary is quite a simple application, and any major design would probably be a waste of time.
That said, navigating an existing program’s design can be very useful for improving understanding, and Xcode has this functionality built right in. You can create class diagrams, which are maps of the relationships between the classes of a project. Here is how you do it:
- Open an Xcode project, like Unitary.
- Select a group or file in the Groups & Files list on the left. The class diagram will be created for your selection. If you select the whole project, you will get a class diagram with all classes, including the built in Cocoa classes. The best is probably just to choose the ‘Classes’ group, which contains the classes for the project itself.
- Go to the Design menu, and choose Quick Model from the Class Model submenu.
You should be presented with a diagram that looks like the one below. Note that this diagram is more than just a pretty face — you can use it to navigate to files, and even view the methods of a class. Arrows point from a subclass to its superclass, or from a category to its class. For example, the ‘(Abstract)’ box in the diagram below represents the Abstract category of the Conversion class that we wrote last time. Spend a little time playing with it before moving on with the rest of the tutorial.

Xcode Action
Now let’s move onto this week’s main task, adding a pop up button to Unitary. Begin by locating the finished source code from last week, or downloading it. Double click the project file to open it in Xcode.
First locate the UnitaryController.h file in the Classes group of the Groups & Files tree on the left, and select it. Edit it so that it looks like this
#import <Cocoa/Cocoa.h>
@class Conversion;
@interface UnitaryController : NSObject {
IBOutlet NSTextField *firstTextField;
IBOutlet NSTextField *secondTextField;
IBOutlet NSTextField *firstFieldLabel;
IBOutlet NSTextField *secondFieldLabel;
IBOutlet NSPopUpButton *conversionPopUpButton;
NSArray *conversions;
Conversion *selectedConversion;
double firstValue, secondValue;
}
-(IBAction)convert:(id)sender;
-(IBAction)changeConversion:(id)sender;
@end
I have added a single action, changeConversion:, which will be invoked when the pop up button is used. The name of the text field variables have been changed to be more generic; previously, they were named after temperature units, and we now want to be able to use arbitrary unit types. New outlets called firstFieldLabel and secondFieldLabel have also been added; these outlets will be connected to the labels that delineate which units are being converted, and will be updated according to the conversion selected in the pop up button.
A few more changes have also taken place. The temperatureConversion instance variable from last time has been replaced with an NSArray, which will hold objects representing all of the possible conversions, and a selectedConversion instance variable added to point to the particular Conversion object currently in effect. Also, the numbers taking part in the conversion are now stored in the instance variables firstValue and secondValue. Although at this point it is not very advantageous to do this, it is good practice to represent data explicitly in the model layer, rather than only in the view, and it will simplify things in the long run.
Now open the UnitaryController.m file, either by selecting it in Groups & Files, or by clicking the small box shaped icon at the top-right of the source editor. (This box changes between the header and implementation files of a given class, and is a good short cut to know.)
Rewrite the UnitaryController.m file to look like so
#import "UnitaryController.h"
#import "Conversion.h"
#import "TemperatureConversion.h"
#import "EnergyConversion.h"
@implementation UnitaryController
-(void)awakeFromNib {
[firstTextField setFloatValue:0.0];
[secondTextField setFloatValue:0.0];
// Create conversion objects
Conversion *temperatureConversion = [[TemperatureConversion alloc] init];
Conversion *energyConversion = [[EnergyConversion alloc] init];
conversions = [[NSArray arrayWithObjects:temperatureConversion, energyConversion, nil] retain];
[temperatureConversion release];
[energyConversion release];
// Synch the text fields and popup by 'faking' a selection in the popup
[self changeConversion:conversionPopUpButton];
}
-(void)dealloc {
[conversions release];
[super dealloc];
}
-(IBAction)convert:(id)sender {
firstValue = [firstTextField doubleValue];
secondValue = [secondTextField doubleValue];
if ( sender == firstTextField ) {
secondValue = [selectedConversion secondValueForFirstValue:firstValue];
[secondTextField setDoubleValue:secondValue];
}
else if ( sender == secondTextField ) {
firstValue = [selectedConversion firstValueForSecondValue:secondValue];
[firstTextField setDoubleValue:firstValue];
}
}
-(IBAction)changeConversion:(id)sender {
unsigned conversionIndex = [conversionPopUpButton indexOfSelectedItem];
selectedConversion = [conversions objectAtIndex:conversionIndex];
[firstFieldLabel setStringValue:[selectedConversion firstUnitName]];
[secondFieldLabel setStringValue:[selectedConversion secondUnitName]];
// Invoke convert: method to update the values on display
[self convert:firstTextField];
}
@end
This bears similarities to the code from the last tutorial, but also has some significant changes. The awakeFromNib method now creates an array of Conversion objects, and stores them in the conversions instance variable. These will correspond to the labels in the pop up button we will create below. NSArray is a very important Cocoa class, and I suggest you read up on it in the documentation. The fastest way to do this is to hold down the option key, and double click on ‘NSArray’ in the source.
The array is created using the arrayWithObjects: method, rather than the usual alloc/init approach. Many Cocoa classes have these ‘convenience initializers’. It is important to realize that they return an autoreleased object, which will be deallocated unless you explicitly retain it, as we do here. You will need to use arrayWithObjects: often if you do any Cocoa development; it takes a comma-delimited list of objects, and is terminated by a nil argument. (Don’t forget the nil. It is a very common newbie mistake.)
The convert: action is similar to last time, but makes use of the more generic variable naming that has been introduced, and uses the selectedConversion rather than the hard coded temperatureConversion object.
The new action, changeConversion:, is invoked whenever a selection is made in the pop up button. As you can see, the index of the item selected in the pop up button is retrieved using indexOfSelectedItem, and this is used with the conversions array to set the selectedConversion. The firstUnitName and secondUnitName methods of the abstract Conversion class are then used to set the labels in the interface.
Hopefully you are starting to appreciate the beauty of polymorphism at this point: The changeConversion: method knows nothing about the Conversion objects it is working with, other than that they all derive from the Conversion class. In fact, the only information about the concrete conversions is included in awakeFromNib, where they are initialized. Thereafter, only the abstract interface of the Conversion superclass is required.
Keeping the view in synch with the model is the task of the controller. You have to do all of this yourself when programming purely with the Outlet/Action/Target approach. (The newer Cocoa Bindings technology, which we will take a look at shortly, can relieve you from a lot of this.) Note how the convert: action is invoked at the end of the changeConversion: action, and the changeConversion: action is invoked in awakeFromNib. Both of these invocations are designed to update the view and model so that they are in synch and in a valid state. You need to do this sort of ‘synching’ quite a bit in your controller classes.
Finally, you will note that there is a second conversion class included in the source code: EnergyConversion. I have added this purely to demonstrate that everything works when you have multiple conversions. You can add as many as you like. The source code for EnergyConversion is very similar to that of TemperatureConversion, so I won’t reproduce it here. You can view it by downloading this week’s project source.
Creating a Pop Up Button
Now we are ready to head back into the view layer. Locate the MainMenu.nib file in the Classes > Resources group of the Groups & Files tree. Double click it to open Interface Builder, and follow this procedure:
- Make the Unitary window bigger by dragging the bottom-right corner.
- Click and drag over the existing controls to select them, and then drag them to the bottom of the window, leaving a space at the top for the pop up button.
- Go to the Controls & Indicators tab in the Palettes panel. This is usually second from the left. (In the screen shot, it is third from the left.) If the Palettes are not visible, choose the Tools > Palettes > Show Palettes menu item.

- Drag the
NSPopUpButtonfrom the palette to the Unitary window, and rearrange the window so that it looks something like the screenshot below.

- Double click the pop up button to open it.
- Double click the ‘Item1’ menu item, and replace the text with ‘Temperature’.
- Repeat for for ‘Item2’, but enter ‘Energy’ instead.
- Click once on ‘Item3’, and press the Backspace key to delete it.
- Click on the Temperature item to select it, and then click outside the pop up button to close it.
- Save your changes.
Entering the conversions explicitly in the pop up button is not the most flexible approach, but I wanted to show you how you edit a pop up button in Interface Builder. It would be better to populate the button programmatically in awakeFromNib, using the Conversion subclasses initialized there. That way, whenever a new conversion class was added, the pop up button would be automatically kept in synch.
Connecting it Up
Now we need to tell Interface Builder about the changes we made to the UnitaryController class, and then connect up the new outlets and actions we have created. To update UnitaryController in Interface Builder, locate the UnitaryController.h header file in the Xcode project, and drag this onto the MainMenu.nib window in Interface Builder. It should prompt you about how you want to merge the new header information with the old; just click the Replace button to remove the old information completely.
To connect up the outlets and actions, follow this procedure:
- Click on the Instances tab of the MainMenu.nib window in Interface Builder. If the Unitary window is not visible, double click on the Window instance to make it appear.
- Control-Drag from the UnitaryController instance to the pop up button, select the
conversionPopUpButtonin the Outlets tab of the inspector, and click the Connect button in the Inspector. - Repeat this process to connect up the
firstTextFieldandsecondTextFieldoutlets. - Lastly, repeat this procedure to connect up the label text fields
firstTextLabelandsecondTextLabel.
When you are finished, all outlets should have a dot next to them in the Inspector, indicating that they are connected.

You should not need to reconnect the convert: actions from last time, because they should still be intact, but you will need to connect up the pop up button action:
- Control-Drag from the pop up button to the UnitaryController instance.
- Select the Target/Action tab in the Inspector.
- Select the
changeConversion:action, and click the Connect button.
Now, whenever a selection is made in the pop up button, the changeConversion: action of UnitaryController will be invoked.
Are You Converted?
That should be it. Save your changes in Interface Builder, head back to Xcode, and click Build and Go in the toolbar. Hopefully Unitary will start up, and you can start entering values for conversion. Note what happens to the labels and values when you select a different conversion.
You can download the completed source code for this week here. Next time we might take our first look at Cocoa Bindings, but don’t hold me to it. Before then, why not add a few more of your own conversion classes to Unitary.



Comments
Thanks!
This has been a fantastic tutorial so far. I tried going through Apple's Cocoa documentation a while ago but gave up because none of it stuck - your explanation has been much easier to digest.
I'm anxiously awaiting the next installments!
Question about Class diagram
I enjoy your tutorial. But I have a question for you about the generated Unitary class diagram. Even though the class table acknowledges NSObject as superclass to Conversion, the diagram does not draw an arrow from the Conversion block toward the NSObject block. I am relatively new to the use of Xcode but I have meticulously replicated your code and I am at a loss to why this may be occurring. The project runs as it should except for this cosmetic deviation.
OS 10.4.11; Xcode 2.5 on PPC G4