Creating a Cocoa AppController Class
Volume Number: 21 (2005)
Issue Number: 1
Column Tag: Programming
Getting Started
by Dave Mark
Creating a Cocoa AppController Class
In our last Cocoa column, we downloaded the latest and greatest version of Xcode. We created a
Foundation Tool, which is an Objective-C program with a console-based interface.
This month, we're going to build a Cocoa app with an interface we designed using Interface
Builder. The app will use Cocoa's NSSpeechSynthesizer class to speak a line of text. We'll add a
pushbutton to start the speech and another to halt it, even in mid-sentence. The example comes from
Chapter 4 of Aaron Hillegass' book, Cocoa Programming for Mac OS X. We'll start by taking a look at
a class diagramming approach Aaron uses throughout the book.
A Diagram Speaks a Thousand Words
Before we actually start the process of building our project, take a look at the object diagram
shown in Figure 1. This diagramming convention was developed by Aaron Hillegas and I find it works
quite well at describing the interrelationships between the classes, objects, methods, and instance
variables that come together to make your program work.
The class at the heart of this example is the AppController class. Note that this class features
two methods: sayIt: and stopIt:. In Hillegas' drawings, each box represents a class and each arrow
connecting two boxes represents the control-dragging connection you create in Interface Builder. For
example, in Figure 1, note that 4 of the 5 classes are Cocoa classes (they start with NS). All of
our code will be built into a new class that we create called AppController. We'll create two
instances of NSButton, one labeled Say it and one labeled Stop. Each of the buttons will target one
of the two AppController methods. When we build the project in Interface Builder, we'll create an
instance of AppController, then control-drag from each button to the AppController instance and
double-click on the method we want called to finish the connection.
We'll also add instance variables to AppController to keep track of the NSTextField (so we can
retrieve the text to say it) and the NSSpeechSynthesizer (so we can send it the text to start
speaking and send it a stop message to halt the speaking).
Figure 1. An object diagram
for the first incarnation of our SpeakLine program.
Create the SpeakLine Project
Launch Xcode and create a new project using the Cocoa Application template. Name the project
SpeakLine.
Editing the .nib File
In your SpeakLine project file, in the Groups & Files pane, find the file MainMenu.nib and
double-click it to launch Interface Builder. You can find the file in the NIB Files group as well as
under the SpeakLine group, in the Resources subgroup.
Once Interface Builder launches, click on the third icon from the left in the palette window,
then drag an NSTextField from the palette onto the main window. As you can see in Figure 2, the
NSTextField is in the upper-left corner of the set of text items.
Figure 2. Dragging out an
NSTextField.
Drag the NSTextField so it is almost as wide as the window (so the dashed blue line appears when
you get about a scrollbar's width from the right side of the window). Double-click on the text field
and change its text to read Peter Piper picked a peck of pickled peppers or, if you are by yourself,
perhaps something a bit more spicy.
Next, click on the second icon from the left at the top of the palette window to show the control
palette items. Drag two buttons onto the window, below the NSTextField, with the proper spacing
between them and the right side of the window. Label the right button Say It and the left button
Stop (double-click on a button to edit its label).
Finally, resize the window itself, making it as short as possible. Figure 3 shows my Interface
Builder session. In this picture, I am dragging the Stop button into place. You can see the dashed
blue lines showing that the two buttons are aligned with each other and that the Stop button is the
correct distance from the text field above it and the Say It button to its right.
Figure 3. Use the blue
dashed lines to line up your buttons and NSTextField.
Create the AppController Class
Now that your interface is laid out, it's time to create the new AppController class.
Click on the MainMenu.nib window and click on the Classes tab. Scroll all the way to the left and
click on the NSObject class. With NSObject highlighted, select Subclass NSObject from the Classes
menu. Name the new subclass AppController (see Figure 4).
Figure 4. Click on the
NSObject class and select Subclass NSObject from the Classes menu.
Now you'll add two actions (one for each button) and an outlet (an instance variable that points
to the text field) to AppController. Open the Info window by selecting Show Info from the Tools
menu. Click on the AppController class in the classes tab in the MainMenu.nib window, then click on
the Info window and select Attributes from the popup near the top of the Info window.
Click on the Actions tab, then click on the Add button at the bottom right of the Info window.
When the new action appears, name it sayIt:, then click Add and name the second action stopIt: (see
Figure 5).
Figure 5. The Info
window, showing the AppController class attributes.
Next, click on the Outlet tab and click Add to add an outlet named textField to AppController.
Click in the Type column and select NSTextField to set the textField type to NSTextField instead of
the generic id.
If you look back at Figure 1, you'll see that we've addressed 3 of the 4 arrows in the object
diagram. We'll add the missing outlet, speechSynth, in code in just a minute.
Be sure that the AppController class is selected in the Classes tab and select Create Files for
AppController from the Classes menu. This will generate two source files (AppController.m and
AppController.h) in your Xcode project which we'll edit in a bit.
Next, create an instance of the AppController class by selecting Instantiate AppController from
the Classes menu. Interface Builder will switch the MainMenu.nib window to the Instances tab and a
new, blue cube will appear with the name AppController.
As you can see in Figure 6, the AppController instance is represented by a blue cube. The tiny
exclamation point in a circle to the lower right of the blue cube tells you that there is at least
one unconnected outlet. Let's take care of that now.
Figure 6. The new
instance of AppController with an unconnected outlet.
Making Connections
Before you start making your connections, take a quick peek back at Figure 1. There are four
connections that need to be made. Three of them will be made by control-dragging. The fourth
(speechSynth) will be made in code.
First, we'll connect AppController's textField outlet so it points to the NSTextField in the main
window. Make sure the Info window is open before you start your drag.
Control-drag from the AppController blue cube to the text field in the main window. When you
release the mouse button, the Info window should display its Connections pane and list the textField
outlet. Either double-click on the textField line or make sure it is selected and click the
Connect button in the lower-right corner of the Info window (Figure 7).
Figure 7. Click the
Connect button to connect the AppController to the textField.
Next, we'll connect the two buttons to their respective AppController methods. Control-drag from
the Say It button to the AppController cube then, in the Info window, connect to the
sayIt: method.
Now control-drag from the Stop button to the AppController cube and connect to the
stopIt: method.
NSWindow's initialFirstResponder
The last bit of Interface Builder work we'll do is to set the NSWindow initialFirstResponder
outlet to point to the text field. This tells the window that you want the text field to be active
when the window appears so you don't have to click in the text field to start typing. To get a feel
for this, try running the program with the initialFirstResponder connected and then with it
disconnected to see what happens.
Control-drag from the Window icon (to the left of the blue AppController cube) to the text field.
In the Info window, click on the initialFirstResponder outlet and click the Connect button.
Now let's type in the code!
Enter the AppController Code
Head back over to Xcode and edit the AppController.h file. We'll add the declaration of
speechSynth:
#import <Cocoa/Cocoa.h>
@interface AppController : NSObject
{
IBOutlet NSTextField *textField;
NSSpeechSynthesizer *speechSynth;
}
- (IBAction)sayIt:(id)sender;
- (IBAction)stopIt:(id)sender;
@end
Next, edit AppController.m to look like this:
#import "AppController.h"
@implementation AppController
- (id)init
{
[super init];
NSLog( @"init" );
speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
return self;
}
- (IBAction)sayIt:(id)sender
{
NSString *string = [textField stringValue];
if ( [string length] == 0) {
return;
}
[speechSynth startSpeakingString: string];
NSLog( @"Have started to say: %@", string );
}
- (IBAction)stopIt:(id)sender
{
NSLog( @"stopping" );
[speechSynth stopSpeaking];
}
- (void)dealloc
{
NSLog( @"dealloc" );
[speechSynth release];
[super dealloc];
}
@end
Build and run the application. Notice that you can click the Stop button to stop the speaking,
even in the middle.
Take a look through the code. Most of it should make sense, especially if you've been following
along with my previous Cocoa columns.
The init: method calls the superclasses' init drops a message to the console, creates an instance
of the NSSpeechSynthesizer, then returns a pointer to itself.
sayIt: sends a stringValue message to textField to retrieve the text, then, if there's at least
one character in the field, send it via a startSpeakingString message to speechSynth. The string is
sent to the console as well, just to help you follow along.
stopIt: sends a message to the console, then sends a stopSpeaking message to speechSynth.
dealloc: is called when the AppController object is released. You'll likely never see the console
message, since the AppController object was created automatically and is never sent a release
message. When it is loaded from the .nib file, the AppController instance has a ref count of one.
Not a big deal, but worth noting.
Till Next Month...
One thing that Aaron does in his book is add a color well to the program so the user can choose
their own text color. See if you can do this on your own. You'll want to take advantage of the
NSColorWell class.
Be sure to check out http://www.spiderworks.com and I'll
see you next month...
Dave Mark is a long-time Mac developer and author and has written a number of books
on Macintosh development, including Learn C on the Macintosh, Learn C++ on the Macintosh, and The
Macintosh Programming Primer series. Dave's been busy lately cooking up his next concoction. Want a
peek?
http://www.spiderworks.com.