Now that we’ve set up our user interface, we’re ready to connect it to
our code. To do this, go back to the HelloUserViewController.xib document
window and its three icons. We’re going to be working with the File’s
Owner icon. This isn’t a real object but, instead, a proxy object that
represents an object assigned to be the “owner” of the nib when the nib
is loaded.
WORKING WITH XCODE AND INTERFACE BUILDER 46
Xcode has already set the class of File’s Owner as HelloUserViewController
(which you can examine with the Identity inspector, D4), and this
is key to getting our code connected to our interface. The object that
owns this nib and its customized view is a HelloUserViewController, and
we just created actions and outlets in that class by editing its header
file. That means IB knows that the File’s Owner will have those outlets
and actions at runtime.
Because we’ve added those IBOutlets and IBActions, when you right-click
(or Ctrl+click) the File’s Owner to see its outlets, you’ll see helloLabel
and nameField.3
Now we’re going to actually make the connections. In the gray headsup-
display (HUD) window’s list of outlets, click the circle next to helloLabel,
and drag to the label in the preview window. As you hover over
the label, IB will show a box around it and open a window identifying
your proposed connection as Label (Label), meaning that you’re about
3. You can also examine an object’s outlets and received actions in the Connections
inspector (D2).
WORKING WITH XCODE AND INTERFACE BUILDER 47
to connect to a label (specifically a UILabel), whose contents are currently
the text “Label.” Lift up on the mouse button to complete the
drag and make the connection; the name of the connected component
will appear back in the HUD window.
Now do the same thing with the nameField outlet, whose drag gesture is
shown here:
You can connect the action with a similar gesture in the opposite direction,
from the widget to the File’s Owner. Right-click the button to show
its list of events,4 and drag a connection from the Touch Up Inside
event to the File’s Owner. When you release the mouse, a small HUD
window will pop up and show the declared actions that you can connect
to. Since you declared only one IBAction, it’s your only choice here:
sayHello:.
This completes the wire-up of outlets and connections in Interface
Builder. To keep things clean, you might want to set the label’s title to
an empty string so the application doesn’t say “Label” when it launches.
You’re now done with IB and can save and quit.
4. Again, you could also see the list of connections with the Connections inspector. As a
shortcut, Ctrl+dragging from a button to a receiver object connects the Touch Up Inside
event without even bringing up the list of events.
WORKING WITH XCODE AND INTERFACE BUILDER 48
Implementing the Action
Now you have a GUI, with connections from the IB-created widgets to
instance variables in your custom class. If you run it right now, a tap on
the Say Hello button will result in a call to sayHello:. Thing is, we haven’t
written that method yet, so our app will crash with an “unrecognized
selector” error. Let’s deal with that now.
Your method implementations go in the class’s implementation file,
which has a .m filename extension. Open HelloUserViewController.m from
the project window in Xcode. The method can go anywhere between the
lines containing the @implementation and @end keywords. Type in the
following method implementation in the line following @implementation
HelloUserViewController:
Download HelloiPhone/HelloUser/Classes/HelloUserViewController.m
Line 1 - (void) sayHello: (id) sender {
2 NSString *userName = nameField.text;
3 NSString *helloMessage =
4 [[NSString alloc] initWithFormat: @"Hello %@" , userName];
5 helloLabel.text = helloMessage;
6 [helloMessage release];
7 nameField.text = NULL;
8 }
• On line 2, we refer to the instance variable nameField and then get
the text that’s been typed into it. Since UILabel provides text as a
property, we get and set it with the dot operator. We’ll have more
to say about properties a little later in the chapter.
• Next, we create a new hello message by creating a new NSString
on line 3. As is usually the case with Cocoa objects created in
code, we first allocate memory for the object with alloc, and then
we initialize its contents. To create the string substitution, we can
use a format string and substitute in the user value. The leading
@ lets us quickly allocate a static NSString for the format, which is
just Hello %@. The %@ is a format specifier that allows us to insert
a string representation of any Cocoa object.5 Here, we insert the
value of userName.
• With the format string prepared, we set it on the label in 5. We do
this by getting the helloLabel instance variable and setting its text
property.
5. Formatting strings and other format specifiers is described further in Section 27.5,
Logging to Standard Output , on page 498.
WORKING WITH XCODE AND INTERFACE BUILDER 49
• Finally, we have one little bit of housekeeping. We allocated memory
for a string on line 3. As we no longer need this string, we are
obligated to release it in order to free up the allocated memory.
On line 6, we do this by sending the release message to the string.
Technically, this doesn’t free the memory; it says that we are no
longer interested in the object, so if nobody else is claiming the
string, it will be freed (it’s not freed, by the way, since the UILabel
also retained it when we set it as the label’s text). We’ll discuss the
memory management system in detail later in this chapter.
Our application is now ready to run. Click Build and Go to run it in the
simulator, and the resulting application will allow you to click in the
text field, type your name, and tap Say Hello to display your name like
this.
ANATOMY OF YOUR IPHONE APPLICATION 50
3.5 Anatomy of Your iPhone Application
So far, you’ve had to endure a bit of “type this here, do that there, never
mind the little man behind the curtain”-type instruction, so let’s step
back and take a look at just how your application gets launched and
how all these pieces interact. If this ends up being more detail than
you need, you don’t need to worry about it; we’re covering it in order to
demystify the nature of how an iPhone app works and why the project
template is structured like it is.
As described earlier in Section 3.4, Working with Xcode and Interface
Builder, on page 40, the main.m file has the implementation of the main( )
function that the system calls to launch your application. Open it up if
you like, and you’ll see it’s just four lines:
Download HelloiPhone/HelloUser/main.m
Line 1 int main(int argc, char *argv[]) {
2
3 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
4 int retVal = UIApplicationMain(argc, argv, nil, nil);
5 [pool release];
6 return retVal;
7 }
The signature on line 1 is a typical C main( ) function, designed to be
called from the command line with a variable number of arguments.
Line 3 sets up an autorelease pool for memory management (which
will be explained later in Section 3.7, Managing Application Memory, on
page 56), and line 5 releases it when the application ends.
On line 4, we call the UIApplicationMain function to start up the main
event loop and start your application. The first two arguments pass the
command-line arguments (if any), while the third and fourth indicate
the application’s main class and its application delegate, which is a
class in your project that handles application life-cycle events. If these
are nil, as they are here, then UIKit assumes that it needs to load the
application from a nib file.
The Info.plist file provides the application’s main nib bundle, typically
MainWindow.xib. So, UIKit expects to find the app delegate in this nib.
Double-click MainWindow.xib to open it in Interface Builder.
ANATOMY OF YOUR IPHONE APPLICATION 51
You should see something like this:
The nib contains an app delegate icon (HelloUser App Delegate), a view
controller (HelloUser View Controller), a window (Window), in addition
to the File’s Owner and First Responder proxy objects that are always
present.
Open the Connections inspector (D2) for the application delegate object,
and you can start to see how the pieces fit together. Every iPhone
application must have a single UIWindow object, and the application delegate
has a connection to the nib’s window object. It also has a connection
to a view controller object, which is an instance of the HelloUserView-
Controller class that we customized earlier in the chapter. And as you
saw before, the view controller provides the logic associated with an onscreen
view, the one that we customized in the HelloUserViewController.xib
file. In fact, if you investigate the view controller with the Attributes
inspector, you’ll be able to see where it refers to that nib file, and if
you double-click the view controller, the view’s preview window says it’s
“Loaded from HelloUserViewController.”
CUSTOMIZING BEHAVIOR WITH DELEGATION 52
window
MainWindow xib
App Delegate
View Controller Window
File's Owner delegate
viewController
HelloUserViewController xib
File's Owner
nibName
class
U Application
class
HelloUserView
Controller
class
HelloUserApp
Delegate
So, to summarize, the system starts by calling the main( ) function,
which calls UIApplicationName( ), which uses Info.plist to look up the main
nib, in which it finds an application delegate (connected to the app’s
one window) and a view controller, which loads from another nib file.
Your application’s first chances to provide custom logic come in the
life-cycle methods associated with the app delegate (such as application-
DidFinishLaunching:) and the view controller it loads (which gets callbacks
like initWithCoder: when it’s loaded from the nib and viewDidLoad when
its view is loaded). But in our application, we don’t need to do anything
until the button is tapped. You’ll probably never want or need to
mess with main.m or MainWindow.xib, because these life-cycle callbacks
offer more convenient points to insert start-up code, but now you know
how all the stuff in your Xcode project works together to bring up your
application.
3.6 Customizing Behavior with Delegation
The application delegate is an example of one of Cocoa’s most significant
design patterns: delegation.
CUSTOMIZING BEHAVIOR WITH DELEGATION 53
The idea of delegation is that an object can have a single delegate object
that it will call when certain events occur. From the delegate’s point of
view, it’s sort of like a callback or notification scheme:6 “Call me when
this stuff happens.” From the delegating object’s point of view, it’s more
like handing off responsibility: “I don’t know what, if anything, needs to
happen when this event occurs, so you deal with it.”
In the case of the application delegate, the object that UIApplication-
Main declares as the application delegate gets callbacks when various
events that affect the whole application occur: when it’s launched from
the home screen, when it’s launched with a URL by another application
(see Chapter 26, Application Integration, on page 482), when it’s
warned of a low-memory condition, and so on. This is an example of
a formal delegate arrangement, one that is defined by an Objective-C
protocol, UIApplicationDelegate. This protocol has its own documentation
page, like a regular Cocoa class, but instead of describing how an existing
class’s methods work, it describes when the delegate methods will
be called and what you as an implementor of those methods could or
should do when they are. For a class to actually become an application
delegate, it needs to declare in its header file that it implements
the protocol and then implement any delegate methods not marked as
“optional.”7
There are a few delegate protocols that we could add to Hello User to
make it behave more like a typical iPhone application. For example,
you might have noticed when you ran the application that the keyboard
didn’t disappear when you tapped the Say Hello button, and the Return
key did nothing. These are common tasks for iPhone applications, and
the latter requires us to provide a delegate to the text field.
To dismiss the keyboard, you need to tell the text field to give up its role
as the “first responder,” meaning the component that initially receives
the user input. It’s easy enough to do this when the button is tapped;
just add the following line to the bottom of sayHello:.
[nameField resignFirstResponder];
6. Cocoa also has a notification system that works with zero to many listeners, but
its design and use are profoundly different from delegation, and the two are in no way
interchangeable.
7. To Java programmers, Objective-C protocols are like Java interfaces but with the
potential for optional methods.
CUSTOMIZING BEHAVIOR WITH DELEGATION 54
But what do we do when the Return key is tapped? That’s not a button
that comes from any of our GUI components—the keyboard is provided
automatically by the text field—so there’s no apparent way of wiring up
an event-handling method to it. However, if you look up the documentation8
for UITextField, you’ll see that it has a delegate property that is
defined by the UITextFieldDelegate protocol, which is a defined group of
related methods. Look up this protocol, and you’ll see it has numerous
methods that alert the delegate of events relating to the text field. One
of them is textFieldShouldReturn, which looks like what we need in order
to know when the user has tapped Return.
You indicate that an object implements some or all of the delegate
methods by specifying the delegate name in the @interface header inside
angle brackets following the name of the class that this class extends.9
For our purposes, HelloUserViewController makes a good choice, since it
already knows about the text field. So, declare that it implements the
delegate protocol by editing the HelloUserViewController.h header file and
adding the protocol, in angle braces, to the class declaration, like this:
Download HelloiPhone/HelloUser/Classes/HelloUserViewController.h
@interface HelloUserViewController : UIViewController {
Now you need to set the view controller as the text field’s delegate.
You can do this in code, but since we’ve defined the rest of our GUI
in Interface Builder, it makes sense to set the delegate with IB too.
Double-click HelloUserViewController.xib to open it in IB. By right-clicking
the text field or using its Connections inspector, you’ll see there’s an
empty connection called delegate. Drag from the circular connection
point to the File’s Owner (which represents the HelloUserViewController,
because it’s the object assigned to own this nib), and you’ll declare the
HelloUserViewController as the text field’s delegate. Be sure to save before
leaving IB.
With the view controller set as the text field delegate, it will get eventdriven
callbacks from the text field. Now you just need to provide implementations
for whatever callbacks interest you.
8. See Chapter 27, Debugging, on page 488 for a section on finding class documentation
in Xcode.
9. In some simple cases, there is no formal delegate protocol, and the delegate just
implements some or all of a list of methods listed in the documentation.
CUSTOMIZING BEHAVIOR WITH DELEGATION 55
Here’s a simple method to dismiss the keyboard, which can go anywhere
between the @implementation and the @end in HelloUserViewController.
m:
Download HelloiPhone/HelloUser/Classes/HelloUserViewController.m
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
The signature of this method needs to exactly match the signature
defined by the delegate protocol, so you may want to get in the habit
of copy and pasting from the documentation or the header files if you
choose to look them up.
As you can see, the method is passed a textField (which presumably
is nameField, since our app has only one text field), to which we simply
send the message resignFirstResponder. This tells the text field it’s
no longer going to receive text input (at least not until tapped again),
which will make the text field remove the virtual keyboard. Then we
return YES, because the protocol instructs us to do this if the button
should implement its default behavior for the Return button tap.
Build and Go in Xcode, type in some text, and tap the Return button.
You should see the keyboard disappear (if it doesn’t, verify that you’ve
connected the delegate to File’s Owner in IB and that your method signature
exactly matches the previous code).
It doesn’t update the hello label, because the code to do that is in say-
Hello:, which is called only by the Say Hello button tap. In this common
case, where you want the same code called by both a button tap and the
Return key, you’d usually put the common functionality into a helper
method that both event handlers call.
You might have noticed that one of the nice things about protocols is
that you have to define only as much functionality as you’re interested
in. All the methods in UITextFieldDelegate are optional, so you can ignore
any of the events that don’t interest you. That’s not always the case,
since some delegate protocols declare required methods that the compiler
will insist you implement. Still, it’s a convenient pattern and an
important one that you’ll see again and again in Cocoa.
MANAGING APPLICATION MEMORY 56
3.7 Managing Application Memory
Another defining trait of iPhone applications—one that’s perhaps not as
pleasant as delegation—is dealing with memory management. If you’ve
been working in modern, garbage-collected languages (Java, C#, Ruby,
and even Objective-C on the desktop, for example), you may give little
thought to memory management, because the objects you no longer
have references to get freed without any action on your part. Having to
manually account for your memory use in iPhone applications is often
one of the hardest things that developers have to deal with. Handled
incorrectly, your application leaks memory and ultimately risks being
terminated by the system.
Conceptually, though, memory management in iPhone apps isn’t that
hard to get your head around; it’s the discipline of freeing up memory
that takes some getting used to. Cocoa uses a reference-counting system
that’s actually fairly simple to understand. All objects are allocated
with a reference count of 1. This reference count can be manipulated
with the use of two instance methods: retain increments the reference
count by one, while release decrements it. When the count goes to zero,
the object is ready to be freed.
Along with this “plus one” description, another analogy for memory
management is phrased in terms of interest. If you are interested in
an object that you receive from a method call, you retain it. When you
are no longer interested, you release it.
There is a fundamental rule for Cocoa memory management: you own
any object you create by means of a method that uses the words alloc,
new, or copy. And for any object you own, you must release it at some
point.
The flip side of this rule is that you don’t own any object you receive via
any other means, such as one you get as a value from a method. You
don’t release these objects, because whatever code that created them is
responsible for releasing them.
However, you might want to own such an object. In such a case, you
can retain the object. By doing this, you become an owner and therefore
must release it at some point.
One interesting twist is the idea of autoreleased objects. When you send
the autorelease message to an object, you add it to a pool of objects that
will all be sent a release method sometime in the future, typically when
ACCESSING VARIABLES AS PROPERTIES 57
the event-dispatching call stack returns. Autoreleasing makes a lot of
sense for methods that return objects that they themselves don’t or
can’t manage. With autorelease, the caller gets a chance to retain the
returned object, and if it doesn’t, the object will eventually be released.
You might remember that we had a taste of memory management back
in the sayHello: implementation in Section 3.4, Designing an Interactive
Application, on page 41. We created an NSString with alloc and were
therefore obligated to release it when we were done with it.
Some classes provide convenience constructors that can save you a
step. Rather than the usual alloc and initXXX pair, you can sometimes
use a class method that provides an autoreleased object. For example,
NSString offers a class method stringWithFormat: that works just like the
alloc and initWithFormat: calls, except that the returned string is autoreleased,
meaning you don’t (and shouldn’t) explicitly release it yourself.
We do have a little bit of a leak in our application. We declare retaining
properties for objects that never get released. Granted, these are
IB-created objects that are going to get freed only when the whole app
shuts down. But there’s a good habit we should get into now: when your
object goes away, it should release any object it’s holding references to.
You know when your object is being freed, because its dealloc method
gets called. So, in HelloUserViewController.m, release the objects you have
instance variable references to:
Download HelloiPhone/HelloUser/Classes/HelloUserViewController.m
- (void)dealloc {
[helloLabel release];
[nameField release];
[super dealloc];
}
3.8 Accessing Variables as Properties
One recent addition to Objective-C that you see throughout the iPhone
APIs is the property. We dealt with properties earlier when retrieving the
text from the text field and setting a different text property on the label.
Conceptually, properties are just instance variables with a naming convention
for getter and setter methods. For example, the UILabel class has
a text property that you could set via a setText method and retrieve via
text, though you typically just use the dot operator to do assignments,
ACCESSING VARIABLES AS PROPERTIES 58
so instead of [myLabel setText: myString], you write myLabel.text = myString,
and instead of myString = [myLabel text],10 you use myString = myLabel.text.
Actually, properties are deeper and more interesting than just a naming
convention, since they allow you to declare the memory and threadhandling
behaviors of the property and optionally have the compiler
provide that behavior for you.
You define a property with a statement in the header file of the following
form:
@property (attributes) proptype propname;
Then in the implementation file, you either provide implementations of
getter and setter methods or let the compiler synthesize them for you:
@synthesize propname;
What’s nice about properties is that you can use the attributes to
provide information about your properties that you couldn’t expose
through instance variable or getter/setter methods alone. The most significant
attributes address three traits:
• Readabililty: A property can be readwrite (the default) or readonly.
• Memory management: For setters, the default behavior is a simple
assign, which is appropriate for primitive types. For properties that
are objects, you can use retain to send a retain message to the object
being set (ensuring that your object will be an owner of it), while
also releaseing any previous value. The third option is to copy the
object being set as the property, which also makes you an owner.
• Thread handling: By default, the synthesized getter and setter are
thread-safe. Since most iPhone applications don’t use multiple
threads and just do their work as part of UIKit’s main run loop,
you can declare your properties as nonatomic for a thread-unsafe
but higher-performance implementation.
Let’s see how we declare properties for the helloLabel and nameField.
In HelloUserViewController.h, after the sayHello: method declaration (really
anywhere outside the @interface curly-brace block and before the @end),
add declarations to create nonatomic, retaining property declarations
for the two instance variables we’ve already declared.
10. Notice that by convention Cocoa getters don’t actually use get in the method name.
ACCESSING VARIABLES AS PROPERTIES 59
Properties: Old and New
At the time of this writing, there’s a difference in property support
for the iPhone Simulator and on the actual device, a
consequence of the fact that the device runs the “modern”
Objective-C runtime and the simulator doesn’t. The essential
difference is that the modern runtime does not require you
to explicitly back up a property with an instance variable; it’s
enough to just write the @property statement and let Xcode and
the Objective-C runtime do the rest.
However, if you do this, you’ll find your code won’t compile if
your Active SDK is set to Simulator. For this book, we’re making
everything simulator-friendly by explicitly backing properties
with ivars.
If you want to distinguish a property from the instance variable
backing it up, then you can use an = in the @synthesize.
For example, to use the ivar myTheProp for the property theProp,
you’d use @synthesize theProp = myTheProp; in your .m file. Also,
note that you can declare IBOutlet on either the property or the
ivar. We prefer the former.
Download HelloiPhone/HelloUser/Classes/HelloUserViewController.h
@property (nonatomic, retain) UILabel *helloLabel;
@property (nonatomic, retain) UITextField *nameField;
Now to get those properties created, we just need one line in HelloUser-
ViewController.m, right after the @implementation.
Download HelloiPhone/HelloUser/Classes/HelloUserViewController.m
@synthesize helloLabel, nameField;
With this, our class (and others that call us) can refer to these properties
with the dot operator, which is really convenient when you need to
chain them together, such as when another class needs to call helloView-
Controller.nameField.text.
The UIKit classes use properties extensively, as you may have noticed in
the use of a text property in both the UILabel and UITextField classes. And
if you agree that the properties make the code a little more readable—in
addition to expressing your intentions for readability, memory management,
and threading—then we hope you’ll use them for getters and
setters, as we commonly do throughout the rest of book.
TAKE-AWAY: STUFF TO REMEMBER 60
3.9 Take-Away: Stuff to Remember
We have covered an extraordinary amount of material in this chapter,
having started with no interactivity at all and ending up with some
sophisticated abilities for managing our class’s members.
Rather than simply summarize, we’d like to wrap this chapter with a
list of what we think are some of the most important things you should
remember—stuff that will come up in more or less every iPhone application
you write.
• Although other classes and nibs are loaded earlier as part of the
application startup sequence, you usually want to look at your
application delegate’s applicationDidFinishLaunching: and any view
controllers’ initWithCoder: and viewDidLoad as places to add custom
start-up behavior.
• Objective-C instance variables go in the header file, inside the
@interface declaration, always with the type and, if they’re objects,
the pointer (*) character.
• Objective-C method declarations go in the header file, outside the
@interface block, in the following form: - (returntype) methodName:
(parameter1type) parameter1 parameter2Name: (parameter2type)
parameter2... ;. Alternately, you can put a method declaration (or
only its implementation) in the .m file, if you don’t want other
classes to be able to see it.
• Objects are created in code (usually with alloc and init) or with
Interface Builder. Don’t do both for the same object. . . you’ll actually
be creating two objects.
• To work with objects created in Interface Builder, declare instance
variables as IBOutlets and event-handling methods as returning
IBAction. Then wire the connections in the nib file with IB. Be sure
to save the file in IB before you build the project in Xcode.
• To implement a delegate protocol, add the protocol name in angle
braces to your header file’s @interface statement, after the name of
the class you’re subclassing. Then, in the implementation (.m) file,
implement all required methods as well as any others you’re interested
in. Finally, make your object the delegate either by wiring a
TAKE-AWAY: STUFF TO REMEMBER 61
delegate outlet in IB or by setting a property in code (for example,
textField.delegate = self;).11
• Declare properties with a @property declaration in the header file,
and then use @synthesize in the implementation to have the compiler
create the getters and setters for you.
• Add to the provided implementation of dealloc to release any instance
variables your object may be retaining. That said, your code
should never call dealloc directly; it will be called automatically
when an object is being freed by the Objective-C runtime.
• Finally, remember the fundamental memory rule: you own any
object you obtain by way of a method with alloc, new, or copy
in its name, and you must eventually release (or autorelease) such
objects. You do not own objects obtained through other means and
must not release them, unless you choose to become an owner by
calling retain.
11. We haven’t mentioned self before, but you may have guessed it’s how an object refers
to itself, like self in Ruby or this in Java.
Chapter 4
View Controllers
Each screen in your iPhone application is managed by a view controller
(VC) that is in charge of displaying the view and responding to just
about every action the user can take on that view. Your view controller
is the C in MVC (Model View Controller). It needs to communicate with
both the view and model parts of your application. If you are unfamiliar
with the MVC design pattern, you can get more information from
the Apple documentation at Cocoa Fundamentals Guide: Cocoa Design
Patterns [App06a].
We will start with a view controller that reacts to a tapped button by
logging a message. In other words, we’ll initially have no model and
just use the view controller to interact with the view. Next we’ll add to
our view controller’s responsibilities by building a Movie model class,
creating an instance of it, and then displaying its data on the screen. In
this phase, you’ll see the view controller as the intermediary between
the model and the view. In the third part of the example, we will add
a second view controller and show you how control is passed between
them.
4.1 Implementing a Button Action
Let’s look in a little more detail at a simple view controller. Create a new
project in Xcode using the View-based Application template. Remember,
you get to New Project from the File menu or hit D-B-N. Name the
project Movie.1 Once Xcode is done creating your new project, open
1. Although this initial example has little to do with movies, we will be using this example
throughout the chapter, and the reason to call it Movie will be clear when we start building
the second view controller.
IMPLEMENTING A BUTTON ACTION 63
the MovieViewController.xib file in Interface Builder by double-clicking it.
This file should look familiar. Except for the name, it’s the same file we
started with in Section 2.2, Create the Hello iPhone Project, on page 24.
We are going to add a button to this initial view and have that button
invoke an action method on our view controller. The first step is to add
the button to the view. You will find buttons in the Library. To open the
Library, choose Tools > Library from the menu, or press D-B-L.
You should see something like this screenshot:
There are three ways to view the list of items. You can look at just the
icons, which is the way most experienced UIKit developers prefer since
IMPLEMENTING A BUTTON ACTION 64
it’s smaller and they recognize the icons. You can choose the icons and
a label, which is a little more descriptive. Or you can choose the icon
and description. We recommend starting with the icon and description
because the descriptions will really help you understand what each of
the widgets is and when and how to use them. The previous screenshot
shows the Library open and the pull-down selected.
Choose a Rounded Rect Button, and drag it into the view. Place it near
the bottom, and set its title to Edit by double-clicking in the center of
the button and then typing Edit. You should end up with something that
looks more or less like this:
The button should do something when the user taps it. As you saw
in Section 3.4, Working with Xcode and Interface Builder, on page 40,
there are three steps to making this happen. In Xcode you will declare
a method name in the MovieViewController header file. You will mark it
with the IBAction return type. Then you’ll return to Interface Builder and
connect the button to this method. Finally, you’ll head back to Xcode
to implement the method.
IMPLEMENTING A BUTTON ACTION 65
Open the MovieViewController.h file in Xcode, and add this line to it just
before the @end compiler directive:
Download ViewControllers/Movie01/Classes/MovieViewController.h
- (IBAction)edit;
Save your work in Xcode, and head back to IB to connect the button to
this action. In IB select the button, and open the Connections inspector
(D-2). Drag from the little circle to the right of Touch Up Inside to the
File’s Owner, and then mouse up and select the edit action from the
pop-up window that appears. The button’s Connection inspector should
look like this:
Congratulations, you just made a connection in Interface Builder. We
will be doing a lot of connections like this in Interface Builder. You’ve
just set a target/action pair (for more on target/action, see the sidebar
on the next page). The target is the File’s Owner, and the action is the
edit method. Setting that target/action pair is what causes the button
to invoke the edit method when it’s clicked. We know it can still seem a
bit like magic, but we will pull the curtain back a little bit at a time as
we build more examples and make more connections.
We are done with IB for now, so save your work, quit IB, and switch
back to Xcode. Now we need to implement the method in the MovieView-
Controller.m file. Open the file in Xcode, and add some code between the
@implementation and @end compiler directives that looks like this:
Download ViewControllers/Movie01/Classes/MovieViewController.m
- (IBAction)edit {
NSLog(@"edit method invoked" );
}
IMPLEMENTING A BUTTON ACTION 66
Target/Action Paradigm
IB makes extensive use of the target/action paradigm to make
it easy for us to connect user interface elements to methods
in our code. The target is the object that the control (that is,
a button) will send a message to. The action is the message
that will be sent. So, we say that the target is sent the action
message when the control is activated.
The UIControl defines this mechanism and allows us to connect
several target/action combinations to handle the Multi-Touch
nature of the iPhone interface using one of three method signatures
to respond to events:
• - (IBAction)action
• - (IBAction)action:(id)sender
• - (IBAction)action:(id)sender forEvent:(UIEvent *)event
The first option is what we have chosen for our edit method
because we don’t need any information from the button for
our code to function properly. For other controls (like a slider),
you might want to get the object the user is interacting with
(the sender argument) sent to you so you can use the current
value in your code. Finally, if you need the event, for example,
because you are tracking a Multi-Touch event, you can implement
the third method style to get that extra object.
Whichever approach you take, the target/action paradigm
gives you a great deal of flexibility in how to get controls in the
UI to invoke your code.
This code is invoked when the button is activated because of the connection
we made in IB. It logs the message “edit method invoked” to the
console every time the user releases the button.
Let’s test where we are now; save your changes, and then click the Build
and Go button in Xcode to run the app. When you tap the button, you
should see the log message in the console (D-B-R). The view controller
is responding to a user interacting with controls on the screen. When
we get to more complex interactions such as adding and deleting table
view items in Chapter 5, Table Views, on page 86, the basic interaction
will be the same. The user will tap or drag some view on the screen,
and then the view controller will have a method invoked by that action
and will react.
BUILDING A MODEL 67
4.2 Building a Model
We previously touched on the Model View Controller pattern, but we
have not really built a model to speak of. A real model captures the
essence of your application. In iTunes, classes like Song or Podcast and
the actions they support like play form the model. In iPhoto, it’s classes
like Photo or Album. To build a serious application, you need a real
model. The model does not have to be complex, but we do need something
more than a string. We’ll build a Movie class to play the part of
our model. We will then use an instance of our Movie class to hold the
data and modify the existing UI to display the data. The view controller
will be the glue that is connected to both the model and the view.
Let’s start by creating the Movie class. Select the Classes group in
Xcode, and then right-click or C-click the group and select Add > New
File from the pop-up menu. In the dialog box that opens, choose Cocoa
Touch Classes > Objective-C Class. In the “Subclass of” pull-down,
make sure to select NSObject, and then click the Next button.
Name the class Movie, and make sure to check the two checkboxes for
adding the .h file and adding the file to the target.
Now that we have our Model class defined, we will flesh it out by adding
three properties: an NSString named title to hold the title of the movie,
an NSNumber named boxOfficeGross to hold the box-office sales, and an
BUILDING A MODEL 68
NSString named summary to hold the plot summary text. We’ll declare
three variables between the brackets in our header file that correspond
to these properties and use @property for each so that the compiler will
be able to build the get and set methods for us.
We also want to have a custom init method for our movie so we can
initialize the three properties when we create a new movie. So, add
a method called initWithTitle:boxOfficeGross:summary: to the class as well.
Your header file should look like this:
Download ViewControllers/Movie02/Classes/Movie.h
@interface Movie : NSObject {
NSString *title;
NSNumber *boxOfficeGross;
NSString *summary;
}
- (id)initWithTitle:(NSString *)newTitle
boxOfficeGross:(NSNumber *)newBoxOfficeGross
summary:(NSString *)newSummary;
@property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) NSNumber *boxOfficeGross;
@property(nonatomic, copy) NSString *summary;
@end
Now that we have the interface done, complete the implementation by
adding the @synthesize statements to create the get and set methods for
the properties. We also need to implement the custom init method, and
we’ll need to clean up our memory.
Download ViewControllers/Movie02/Classes/Movie.m
Line 1 @implementation Movie
-
- @synthesize title;
- @synthesize boxOfficeGross;
5 @synthesize summary;
-
- - (id)initWithTitle:(NSString *)newTitle
- boxOfficeGross:(NSNumber *)newBoxOfficeGross
- summary:(NSString *)newSummary {
10 self = [super init];
- if(nil != self) {
- self.title = newTitle;
- self.boxOfficeGross = newBoxOfficeGross;
- self.summary = newSummary;
15 }
- return self;
- }
-
ADDING OUTLETS AND ACTIONS TO THE CONTROLLER 69
- - (void) dealloc {
20 self.title = nil;
- self.boxOfficeGross = nil;
- self.summary = nil;
- [super dealloc];
- }
25
- @end
This implementation has a couple of new concepts. First let’s talk about
the custom init method implementation. We have seen using custom
init methods a couple of times in Chapter 3, iPhone Development Fundamentals,
on page 35, but this is the first implementation we have built
on our own. The code is not complex, but there are a couple of items we
should talk about. In line 10, we’ve assigned [super init] to self. Assigning
self to what the superclass returns from the init method gives the
superclass the opportunity to return a different object. This somewhat
obscure-sounding case shows up semiregularly in some frameworks
(Core Data being the most common). So, it is important to include this
line in your custom init methods. We then set all the properties to the
values passed into our init method. And finally, we return self on the
last line.
The dealloc starting on line 19 takes care of freeing up the memory used
by this object.2 Since the properties are marked as copy, each of them
needs to be released in the dealloc method. We are accomplishing that
by setting the properties to nil. As you learned in Section 3.8, Accessing
Variables as Properties, on page 57, a property that copies or retains
its object value will first release the old value before assigning the new
value. So, in this case we set the property to nil, which first releases the
existing object and then assigns the instance variable to nil.
Fantastic, you have just finished creating your first model class. We
will use an instance of this class to manage the data for our application
through the rest of this chapter. But first, save your work. We need to
update our existing UI to display an instance of Movie.
4.3 Adding Outlets and Actions to the Controller
If you are new to developing with Cocoa, the process we are about to
pursue will feel a little bumpy at first. A typical Cocoa Touch develop-
2. We had our first introduction to memory management in Section 3.7, Managing Application
Memory, on page 56.
ADDING OUTLETS AND ACTIONS TO THE CONTROLLER 70
ment process requires jumping between IB and Xcode frequently. A big
part of all the jumping back and forth is because of the nature of the
view controller. It has one foot in the interface and one in the code.
Since we edit the interface in IB and the code in Xcode, it is natural
that we jump between the two tools. We promise the bumpy feeling
won’t last long. Soon you will build your intuition, and all the jumping
back and forth will make sense (and you’ll even come to like the fluid
nature of it, promise).
Since we are already in Xcode, let’s get started by adding the new properties
we will need to our view controller. Open the MovieViewController.h
file, and add four properties and the instance variables to back them.
This is similar to what you just did in the Movie class. Here you’ll add
one property for the movie object that will be our model. Add three
outlets, one each for the data in the movie object that we are going to
display. Notice where the word IBOutlet appears for each property.
Download ViewControllers/Movie02/Classes/MovieViewController.h
Line 1 #import
-
- @class Movie;
-
5 @interface MovieViewController : UIViewController {
- Movie *movie;
- UILabel *titleLabel;
- UILabel *boxOfficeGrossLabel;
- UILabel *summaryLabel;
10 }
-
- @property(nonatomic, retain) Movie *movie;
- @property(nonatomic, retain) IBOutlet UILabel *titleLabel;
- @property(nonatomic, retain) IBOutlet UILabel *boxOfficeGrossLabel;
15 @property(nonatomic, retain) IBOutlet UILabel *summaryLabel;
-
- - (IBAction)edit;
- @end
The @class compiler directive on line 3 is a forward declaration, and it
tells the Objective-C compiler that you know that it can’t find a class
named Movie and that it should not report errors or warnings about
not being able to find it. Using these forward declarations is a common
practice in header files so that we don’t end up with include cycles
(where one header includes another that also includes the first). We use
forward declarations because the compiler provides poor error reporting
on include cycles. We’d rather get into this habit than have to remember
the bad warnings and what they mean.
UPDATING THE UI 71
Before we leave Xcode, let’s add the @synthesize calls and the import to
the implementation file. Open MovieViewController.m, and add the @synthesize
statement for each of the properties. We always put these statements
at the top of the @implementation block, so we usually do that
first when building a new implementation.
Download ViewControllers/Movie02/Classes/MovieViewController.m
@synthesize titleLabel;
@synthesize boxOfficeGrossLabel;
@synthesize summaryLabel;
@synthesize movie;
Also, for every @class forward declaration you do in the header file, you
almost always have to import the header file for that class in the implementation
file. In this case, that means we have to import Movie.h:
Download ViewControllers/Movie02/Classes/MovieViewController.m
#import "MovieViewController.h"
#import "Movie.h"
Let’s turn our attention to the GUI side of our application, but don’t
forget to save everything before we head over to Interface Builder.
4.4 Updating the UI
Now that we have our header file, let’s go back to Interface Builder
and open MovieViewController.xib. Add three UILabels to our view, arrange
them near the top, and stretch them across the view. Use the blue
alignment lines to help you get them straight. After you place the labels
where you want them, your view should look like the screenshot on the
next page.
Once the labels are placed where you want them, connect the VC’s
outlets to them. We need these connections made so that our view controller
knows about the labels and can place the data from the movie
object into them at the appropriate time.
Connect the outlets to their respective labels. You make a connection
to an outlet in one of two ways. First you can Ctrl+drag from the source
of the connection (where the outlet is) to the destination object. When
you lift up on the mouse, the list of valid outlets is displayed, and you
choose the one you want to set by clicking it. That is all there is to it—
once you click the outlet, the connection is made. Second, you can use
the Connections inspector as you saw in Chapter 3, iPhone Development
Fundamentals, on page 35.
UPDATING THE UI 72
Once the connections are complete, select the File’s Owner, and open
the Connections inspector with D-2. Your inspector should look similar
to this:
We’re now ready to take advantage of all of this prep work to display
the information for a specific movie.
IMPLEMENTING THE CONTROLLER 73
4.5 Implementing the Controller
Now we’re ready update the implementation so that the movie’s data is
displayed on the screen. The first thing we will do is to create the movie
object. Let’s override the viewDidLoad method in the MovieViewController
to create a new instance once the view that the MovieViewController controls
is loaded. Here is the code:
Download ViewControllers/Movie02/Classes/MovieViewController.m
Line 1 - (void)viewDidLoad {
2 [super viewDidLoad];
3 Movie *newMovie = [[[Movie alloc]
4 initWithTitle:@"Iron Man"
5 boxOfficeGross:[NSNumber numberWithFloat:650000000.00]
6 summary:@"Smart guy makes cool armor" ] autorelease];
7 self.movie = newMovie;
8 }
The code is straightforward. Allocate a new instance of Movie, set the
property movie to the new Movie object and then release it. The interesting
thing here is why choose viewDidLoad. To understand the reasoning
behind that, we have to talk a bit about the way view controllers work.
Let’s start in the applicationDidFinishLaunching: method on the MovieAppDelegate
class. This method is called by the UIApplication object when
it’s finished launching. The app delegate has two outlets that are connected
in the MainWindow.xib nib file: window and viewController. The window
property is connected to the main window of our application; the
viewController property is connected to our movie view controller. Here
is the code for applicationDidFinishLaunching::
Download ViewControllers/Movie02/Classes/MovieAppDelegate.m
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
The app delegate is asking the window to add the viewController’s view as
a subview. That is a simple enough looking line of code, isn’t it? Well,
behind the scenes, a bunch of very interesting stuff is going on. When
a view controller is asked for its view, the first thing it does is check to
see whether it already has one. If so, it just returns the already loaded
view. If it does not have one, then it calls the loadView method. That
process runs more or less like this:
• Is a nib filename set (typically set in IB but can be set via code)?
• If so, load the nib file passing the view controller in as File’s Owner.
IMPLEMENTING THE CONTROLLER 74
• After the nib file is loaded, assert that the view has been set. If
not, throw an exception and print an error message.
If a nib filename is not set on your view controller, then you need to
build the view manually in the loadView. Thankfully, we can set the nib
filename via IB, and we rarely if ever need to manually code a loadView
method.3
Once the loadView method finishes, the viewDidLoad method is called.
This is a great place for us to do initialization (such as create our Movie
object) because it’s called only when the view is loaded. If because of
memory warnings we release our view and set it to nil, we can also nil
out the model object. Because we know that when the view is needed
again, the whole process, including viewDidLoad, will be invoked again,
and we will have a Movie object.
The next step in implementing our movie view controller is to implement
the viewWillAppear: method. This method is called every time the view is
about to become visible. Keep in mind that a view can become visible
and then be removed from view several times during the normal flow
of an application. For example, consider the Contacts app; the list of
all your contacts is shown each time you finish looking at or editing an
individual contact, and the viewWillAppear: method is called each time.
This method is the ideal place to set the values we want to appear on
the view when it does appear. Here is the code for our view controller:
Download ViewControllers/Movie02/Classes/MovieViewController.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.titleLabel.text = self.movie.title;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
self.boxOfficeGrossLabel.text =
[formatter stringFromNumber:self.movie.boxOfficeGross];
[formatter release];
self.summaryLabel.text = self.movie.summary;
}
Build and Go to run your application and test it. You should see the
title of the movie that you choose appear in the text fields including a
dollar sign in the box-office gross field.
3. If you want to dig into the MovieViewController’s nib setting, open the MainWindow.xib file,
and inspect (D-1) the MovieViewController. There is more detail in Section 3.5, Anatomy of
Your iPhone Application, on page 50.
CREATING THE NEW VIEW CONTROLLER 75
This code we added places the data for the Movie objects into the text
fields. The one new class in this code is the NSNumberFormatter. We use
formatters to convert back and forth between numbers and strings.
Congratulations! You have successfully built a full-fledged view controller
and the model that it manages. Next you’ll allow the user to
edit the Movie object in a new view controller. You’ll build another view
controller in Xcode, set up its view in IB, and then wire the two view
controllers together.
4.6 Creating the New View Controller
Let’s now create a second view controller that we will use to manage
the modal view. In Xcode, select the Classes group, Ctrl+click the group,
and choose Add > New File from the pop-up menu. In the wizard that
pops up, choose the “UIViewController subclass” item in the iPhone
OS > Cocoa Touch Classes group. Don’t select the “With XIB for user
CREATING THE NEW VIEW CONTROLLER 76
interface” checkbox, because we will be creating our interface shortly.
Click Next, and name the class MovieEditorViewController.
We’re imagining a UI with three text fields and one button, so we’ll
need to add the outlets and actions that we will need to interact properly
with the UI to our header file. The text fields will allow the user
to edit the title, box-office gross, and summary, respectively, and the
button will signal the user is done with the edits. So, we need three
ivar/property pairs, one for each of the text fields. We also need a single
action method for the button to invoke. The last piece of this class
is the instance of the Movie that our user will be editing, so we need
to add an ivar/property pair for that too (don’t forget to add the @class
Movie; forward declaration). The interface for our class should look like
this:
Download ViewControllers/Movie03/Classes/MovieEditorViewController.h
@class Movie;
@interface MovieEditorViewController : UIViewController {
UITextField *titleField;
UITextField *boxOfficeGrossField;
UITextField *summaryField;
Movie *movie;
}
@property(nonatomic, retain) IBOutlet UITextField *titleField;
@property(nonatomic, retain) IBOutlet UITextField *boxOfficeGrossField;
@property(nonatomic, retain) IBOutlet UITextField *summaryField;
@property(nonatomic, retain) Movie *movie;
- (IBAction)done;
@end
Our controller is going to need to respond to keyboard input, so we’ll
add a declaration at the top inside angle brackets for the UITextFieldDelegate
protocol. That tells the compiler that this class intends to implement
all required methods from that protocol. We first saw protocols
in Chapter 3, iPhone Development Fundamentals, on page 35 when we
discussed the UIApplicationDelegate protocol. The text field delegate protocol
is very similar to the application delegate protocol in that methods
are sent to the delegate during interesting points in the text field’s life
cycle. We will be digging in to the details shortly. Make sure to save the
file so Interface Builder will know about the new outlets and actions
we’ve added.
CREATING THE NEW VIEW CONTROLLER 77
Don’t forget to add the import of Movie.h to the top of the MovieEditorView-
Controller.m file. And just inside the @implementation block, we need to
add a @synthesize statement for each of the properties that were declared
in the MovieEditorViewController.h file.
Now we need to implement the viewWillAppear: method to place the values
from the Movie object into the text fields. We’ll set the text property
on the text fields.
Download ViewControllers/Movie03/Classes/MovieEditorViewController.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.titleField.text = self.movie.title;
self.summaryField.text = self.movie.summary;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
self.boxOfficeGrossField.text =
[formatter stringFromNumber:self.movie.boxOfficeGross];
[formatter release];
}
Notice the way that we convert the boxOfficeGross number to a string
via an NSNumberFormatter. We are doing the same thing we did in the
MovieViewController’s viewWillAppear: implementation.
In the done method, we dismiss the current modal view controller with
a call to the dismissModalViewControllerAnimated:
Download ViewControllers/Movie03/Classes/MovieEditorViewController.m
- (IBAction)done {
[[self parentViewController] dismissModalViewControllerAnimated:YES];
}
Though we have not done so yet, when we build the UI, we will make
our view controller the delegate of all the text fields so we can capture
the input via the UITextFieldDelegate protocol. We are going to go over
that implementation now, but the methods won’t get called until we set
the delegate for each text field in IB.
We need to implement two methods of the UITextFieldDelegate protocol.
The textFieldShouldReturn: method is called by the text field when the
user presses the Return button on the keyboard. This is the perfect
spot for us to resign first responder status for the field (which in turn
will cause the keyboard to animate out) by calling resignFirstResponder. It
is typical to return YES from this method, but if you wanted to do some
validation and keep the text field from resigning the first responder
status, you can return NO. But keep in mind that the return value is
CREATING THE NEW VIEW CONTROLLER 78
simply advisory; if the user is taking a call, you won’t be able to prevent
the user from leaving the field, so if your application relies on the data
being valid, this can’t be the only place you check it.
Download ViewControllers/Movie03/Classes/MovieEditorViewController.m
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
The textFieldDidEndEditing: method is called when the text field resigns its
first responder status.4 This method is a great spot to capture the data
that the user typed into the field. We take the values from the fields and
place those values into the movie object. Here is the code:
Download ViewControllers/Movie03/Classes/MovieEditorViewController.m
- (void)textFieldDidEndEditing:(UITextField *)textField {
if(textField == self.titleField) {
self.movie.title = self.titleField.text;
} else if(textField == self.boxOfficeGrossField) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
self.movie.boxOfficeGross =
[formatter numberFromString:self.boxOfficeGrossField.text];
[formatter release];
} else if(textField == self.summaryField) {
self.movie.summary = self.summaryField.text;
}
}
Since this one view controller is playing the part of delegate for each
of the text fields, we need to distinguish which one was typed in so we
know which property of the movie to update.5
Notice also that we are using a currency formatter to convert the data
from the text field to a number for our movie’s boxOfficeGross. This
requires that we put a currency sign in the front of the number for
it to parse properly. In a polished application, we’d want to test for the
leading currency symbol and then choose the proper formatter for what
4. A text field will resign its first responder status because it was told to (as we did
in the previous method) or when it is forced to by the system. The system forces text
fields to resign first responder status when they are taken off-screen. Although in our
example there is no way to send this view off the screen when the keyboard is up, later
in Chapter 5, Table Views, on page 86 you will see how this can happen.
5. As your skill in using the Model View Controller pattern grows, you will likely find
using one controller as the delegate of three text fields to be a little distasteful. We keep
it all in one class here for simplicity’s sake.
the user typed in. We’d also want to ensure that the value we get back
from the formatter is not nil; if it was, we’d want to inform the user the
number was some how invalid. For this app, though, in the interest of
clarity, we are just going to leave it at the currency formatter.
0 comments:
Post a Comment