Cocoa for Scientists (Part VIII): IB, Therefore I am
Author: Drew McCormack
Website: http://www.macanics.net
In this enthralling edition of Cocoa for Scientists, we aren’t going to be writing any code whatsoever. We did that last time in Xcode. Instead, we are going to make our way into the graphical world of Interface Builder (IB). Using IB is more like using Photoshop than Xcode: you drag things onto a window, resize and rearrange them, and connect them together in various ways. It makes making a graphical user interface not only fun, but also a very natural process.
Outlets
Before we get into the nitty gritty, it is worth discussing a little of what you will be meeting today, because it is quite fundamental to how IB and Xcode work together. Last time, we defined some text field variables in the class UnitaryController, and we applied the label IBOutlet to them. I glossed over this at the time, but now you need to know what that is all about.
Outlets in Cocoa are basically just pointers that can be used in Interface Builder. They are typically pointers to graphical interface elements such as text views, tables, etc, but they could also be pointers to any other type of object that you need to use in IB. The idea is that you connect the outlets up in IB to the objects that they need to point at at run time. For example, in the Unitary example we are developing, you should remember that we declared the outlet celsiusTextField and fahrenheitTextField:
@interface UnitaryController : NSObject {
IBOutlet NSTextField *celsiusTextField;
IBOutlet NSTextField *fahrenheitTextField;
}
...
@end
Today we will make these outlets visible to Interface Builder, and connect them up to some text fields in the user interface. That way, the UnitaryController class can use the text fields after the application has launched.
Targets and Actions
Actions are methods that a class defines that can be called by objects in IB; they are declared in source code using the IBAction macro, as described last time. The method itself is the action, and the object that the method belongs to is called the target. Last time, we declared one action, convert:, for the class UnitaryController:
@interface UnitaryController : NSObject {
...
}
-(IBAction)convert:(id)sender;
@end
Actions are typically called when some event occurs, such as a button is pushed, or some text is entered into a text field. For example, in Unitary, when text is entered by the user, the text field will invoke the convert: action of a UnitaryController target object. The text field passes itself as the argument to the action, and this is used by the source code to determine which conversion to perform:
-(IBAction)convert:(id)sender {
if ( sender == celsiusTextField ) {
float celsiusTemperature = [celsiusTextField floatValue];
[fahrenheitTextField setFloatValue:(1.8 * celsiusTemperature + 32.0)];
}
else if ( sender == fahrenheitTextField ) {
float fahrenheitTemperature = [fahrenheitTextField floatValue];
[celsiusTextField setFloatValue:((fahrenheitTemperature - 32.0) / 1.8)];
}
}
If the celsiusTextField is sending the message, it means the user entered something in the text field corresponding to Celsius, and would like to convert to Fahrenheit, and vice versa.
In general, an outlet will be used from within Xcode to tell an object in the interface to do something, and an action/target will be used by an object in Interface Builder to tell an object in Xcode to do something. While this is an oversimplification, it is a good place to start thinking about the distinction between outlets and actions–targets.
What’s in a Nib?
Creating a user interface in Cocoa is somewhat different to just about every other GUI development environment you can find. Other platforms (eg .NET, Java Swing, etc) typically include a graphical tool similar to IB to layout the interface, but the tool in question simply generates source code for the elements in your interface, and you edit that to implement the functionality you want. This system of UI development is largely a requirement of statically-typed languages like C# and Java.
Interface Builder is different, because it doesn’t generate any source code. Instead, it leverages the dynamicism of Objective-C, by directly creating the objects that will be used in the application and serializing them. That is, it turns them into data, and stores them in an XML file. When the application launches, it thaws the frozen objects and connects them up appropriately. The advantage of this approach is that it is very flexible because changing the interface does not mean changing your source code. It also means you can test your interface separate from the rest of the program, because all the objects are available in IB and don’t need to be compiled. We’ll see how you do that shortly.
Adding the UI in Unitary
Enough discussion, let’s charge up Xcode and see how it works in practice. You will probably want to download the source code from last time as a starting point. Then, follow these steps:
- Double click the
Unitary.xcodeprojproject file to open it in Xcode. - Click on the Unitary project icon in the Groups & Files list on the left.
- A list of files from the project should appear in the Details pane on the right. Double click the
MainMenu.nibfile to open it in Interface Builder. (To locateMainMenu.nib, you could also click the disclosure triangle of the Unitary project in Groups & Files, and then open the Resources group.)

Editing the nib file is more like using a drawing program than a programming environment. We’ll begin by changing some attributes of the default window that is included by default:
- Click on the Window object in the MainMenu.nib window, and then bring up the Inspector by either pressing Cmd-Shift-I, or choosing Show Inspector from the Tools menu. (It is worth learning this keyboard shortcut, because you will need it often.)
- Now edit the Inspector so that it looks like the screen shot below. Basically, we are changing the window so that it can’t be closed, the user can’t resize it, the title is Unitary, and it’s position gets automatically saved in the preferences so that it doesn’t jump back to the starting position after a restart. The latter is achieved by giving the window a unique autosave name.

Now we want to add some widgets to the window:
- Click the Cocoa-Text tab of the palette window. This is usually third from left, though in the screenshot below it is fourth from the left.

- Drag two
NSTextFields onto the Unitary window. Also drag two of the labels called System Text Font onto the window. Edit these elements so that the window looks like the screen shot below. You can resize the window by grabbing the bottom right corner and dragging. To change the text in the labels, double click them.

Now we are going to test the interface, without compiling the source code at all:
- Select the Test Interface menu item from the File menu.
- Enter some data in the text fields.
- Quit the test by pressing Cmd-Q.
Connecting up Outlets and Actions
You are almost ready to connect the various interface objects to the code defined in Xcode, but first we need to tell IB about the custom classes we have written, and what actions and outlets they have.
- Go back to Xcode, and locate the file
UnitaryController.hin the Classes group of the Unitary project. - Drag the header file onto the
MainMenu.nibwindow of IB. IB should automatically change to the Classes tab, because you are effectively declaring a new class. - With the
UnitaryControllerclass still selected, choose the Instantiate UnitaryController menu item from the Classes menu in Interface Builder. This should create an instance of the class, which will appear in the Instances tab of the window.

- Open the Inspector again, if it is not already open, and select the
UnitaryControllerinstance in the Instances pane of theMainMenu.nibwindow. - In the Inspector, choose ‘Connections’ from the popup button. In the Outlets tab, you should see the text field variables that we made last time.
Now you are going to use a technique that will become second nature to you over the coming months. It is called control dragging, and it is an essential part of the IB experience.
- Hold down the Control key and drag from the UnitaryController instance in the Instances pane to the Celsius text field in the Unitary window. Let go.
- In the Inspector, choose the
celciusTextFieldoutlet, and click the Connect button. - Repeat this for the
fahrenheitTextField.

Now we have to connect up the actions of the text fields, so that when a user enters a value, the convert: method of UnitaryController will be invoked, and the conversion can take place.
- Control drag from the
celciusTextFieldto theUnitaryControllerinstance in the Instances tab of theMainMenu.nibwindow. - In the Inspector, select the
convert:action, and press the Connect button. - Repeat this for the
fahrenheitTextField. - Save your changes in IB.
Finally, you are ready to build and run Unitary.
- Go back to Xcode and click the Build and Go toolbar button.
- If all is well, Unitary should startup. Enter numbers in the different fields to test it out.
- Quit as per any application, with Cmd-Q.
Signing Off…
That’s the lightening introduction to Interface Builder. You can download the final project for this tutorial here.
Over the coming weeks and months we will spend a lot of time in IB, so you should try to build up a little experience with it. Don’t be afraid to play around with the various UI elements available in the IB palette. It’s also a good idea to make control clicking second nature, and to distinguish outlets from actions–targets in your head.
Next time we will introduce some more complex UI elements as we continue to develop Unitary into a useful utility.



Comments
Excellent!
Thanks for this quick tour through IB. It almost seems too easy to be true... can interface "programming" really be so straight forward?
Pretty Easy
IB does make making a UI pretty easy. Of course, there is a difference between just any UI and a good UI. Learning how to make a good UI has very little to do with the technical aspects, and more to do with how humans work. It can take years to learn to make a good UI. But ultimately it just takes practice.
Drew
---------------------------
Drew McCormack
http://www.macanics.net
http://www.macresearch.org
XCODE 3.0
You will need to update this tutorial as XCODE 3.0 IB behaves very differently. You cannot instantiate a class from IB any more. You have to drag an "object" from the controller section of the pallete, go to the drop down menu and wire the custom classes to the object which will then reflect the outlets and actions that you have defined in the header file.
I suppose you could add the outlets and actions from the IB and ask the IB to generate the header files for you. Is that not what you do in Visual Studio ? You can drop the objects in the lingua franca of VS they are "components" on the surface and then the object would expose its properties and you can wire your custom classes?
The way XCODE does it seems to me a cleaner approach in trying to follow the MVC pattern.
XCODE 3.0
Yes, unfortunately Leopard has made parts of the older tutorials out-of-date. I don't have time to fix them all, unfortunately. Maybe some day...
Drew
---------------------------
Drew McCormack
http://www.maccoremac.com
http://www.macanics.net
http://www.macresearch.org
XCODE 3.0
I think its not that tough get used to with the new IB.
XCode 3 HOWTO
The tutorial works mostly OK in XCode 3, all that's needed is instead of dragging the .h file to the interface builder, drag an NSObject from the Library in IB to the MainMenu.xib window. Then do Cmd-Shift-I and choose the name of the controller / .h file from the list.
Great tutorial all the same!
XCODE 3.0 instructions
I couldn't quite follow what patter was saying, but I did find the answer, of all places, on YouTube.
What you have to do is, as patter said, find the NSObject in the library window (the search box at the bottom is probably the easiest way. Just type "NSObj" and it'll pop up) and drag it over to the MainMenu.nib window. That's the one with the icons that say "File's Owner" and "First Responder", etc. You'll get a generic NSObject class. Now, you can just set the actions and outlets on this object and give it a name. But to do it the way this tutorial suggests (i.e. having already set up the classes in Xcode), select the NSObject in the MainMenu.nib window and open the inspector. In the info tab (the "i" in a circle), select "UnitaryController" from the Class selection drop down. Viola! All of your outlets and actions are loaded. From there on, everything matches up with the tutorial. This procedure makes sense because UnitaryController is a NSObject.
Thank you thank you thank you
Thanks for the comment for XCODE 3.0 instructions. I was able to make Unitary work under Snow Leopard. Really great tutorial.
Still a different way I
Still a different way
I found a UnitaryController object in the classes tab of the palette window. I dragged that one instead of the NSObject on the MainMenu.nib window.
The text fields snow leopard are also a little different. For the editable text I dragged an object (not a class) of type "Text field with number formatter" and for the labels an object of type text field of type "Label".