Sunday, January 30, 2011

Planet Wave Music Apps-Chord & Scale Master from PlanetWaves


Guitar.com a subsidiary of Diadario, puts out music iPhone apps under their company Planet Waves, that cool company you can grab guitar straps and other gear from. They have created some amazing music iPhone apps including, some great ones for guitar players. Check out their app section

Check out one of their newest apps still in Beta...
Chord & Scale Master

Developed by Hank Wallace of Atlantic Quality Design utilizing the Planet Waves ChordMaster chord library by Stephen Salerno and the ScaleMaster scale library written by Marc Schonbrun, Chordmaster/Scalemaster was designed to work best using Internet Explorer on the PC or Safari on both PC and Mac platforms. It can be run using Firefox on the PC, but there have been unresolved compatibility problems with Firefox on the Mac platform.

A handheld model of the ChordMaster library is available from Planet Waves. BUY NOW

An iPhone application for ChordMaster and Scale Wizard is now available through iTunes.

The Web Chordmaster and Scalemaster (WCM) helps you study and play new chords and scales to enhance your guitar skills. Combining ease of access with about 7,800 chords and 1,500 scales, the WCM lets you explore the complete library of guitar chords and scales, quickly hear how groups of chords sound together, and also how scales sound when played against the chords you select. Use the WCM to explore chords you find in sheet music, as a practice aid, and to create exciting new sounds that you can learn to play on guitar. You can also use this application to print out practice sheets for the selected chords and scales.
Playing Chords

To play a chord, select the Root and Variant of the chord from the drop down boxes. The chord appears on the vertical fretboard. Click Play to play the chord using the MIDI device inside your computer. Click the arrow buttons to move the chord to other fret positions on the neck.
Playing Scales

To play a scale, select the Root and Type of the scale from the drop down boxes. The scale appears on the horizontal fretboard. Click Play to play the scale using the MIDI device inside your computer. Click the arrow buttons to move the scale on the neck. Mouse over the clef, to see the notes of the scale selected notated on a staff.
Playing Progressions

Create chord and scale progressions using the area at the bottom of the screen. There are eight slots each for chords and scales. Each lights up with a menu when you move the mouse over it. Use the menu to add, delete, and move chords and scales. Buttons for playing chords, playing scales, or playing both together allow you to hear your progression in various ways. There is a button to allow ALL of the notes for a selected scale to be played for each selected fret position, or else to play only the scale notes starting from the root of the scale.
Suggesting Chords and Scales

A powerful feature of the program is its ability to use intelligence to match chords and scales that will sound musically interesting when played together. Click the Suggest Chords to find chords that are compatible with each selected scale. Click the Suggest Scales to find scales that are compatible with the current chord. Experts in music theory are the brains behind these associations, and all this power is at your disposal for free!
Custom Chords and Scales

Do you want to create your own chords and scales and hear how they sound together? Just click on the fretboard to add and delete notes from the chord or from the scale. Click Play to hear how your creations sound, or to make a progression with your custom choices.
Program Setup

Click the Setup button to control MIDI operation and to control chord arpeggiation.
Help!

Click the Help button for a detailed explanation of program operation, and to learn more about the team that produced this application. Disable the popup window blocker in your browser if the help page does not display.

Tuesday, January 25, 2011

Table Views

Table views are among the most common user interface idioms in
iPhone OS. Many of the default applications use table views extensively,
including Mail, Safari, Phone, iPod, iTunes, and more. In fact, there are
fewer default apps that don’t use tables than those that do. Because of
tables’ utility and convenience and the fact that your user will be thoroughly
accustomed to tables, it is highly likely that your applications
will want to present some of its interface with table views.
In this chapter, we’ll explore how to present data in a table format by
reusing the Movie class from Chapter 4, View Controllers, on page 62.
In that chapter, we used view controllers to present and edit a single
Movie object; here, we’ll use a table view to show many Movie objects
and navigate to a second view controller that will let us edit an existing
object or create a new one.
5.1 Parts of a Table
On the iPhone, a table view is a one-dimensional, top-to-bottom list
of items, optionally split into multiple sections. The sections actually
make the list a two-dimensional data structure. Each section has a
variable number of items, so a given item in a table is identified by its
section and its row within that section.
In Figure 5.1, on the next page, we can see Interface Builder’s presentation
of table views, with dummy data that provides U.S. state names for
section headers and provides cities for row titles. There are two visual
styles for tables: a “plain” style that allows cells to stretch horizontally
to the table’s bounds and a “grouped” style that uses corner-rounding
and indentation to group the rows of each section. The grouped table
PARTS OF A TABLE 87
Figure 5.1: UITableViews in Interface Builder, with “plain” style (left) and
“grouped” style (right)
in the figure shows two sections: one with four rows for the first section
(“California”) and three rows for the second (“New York”).
An iPhone table consists of three things: a view, a data source, and
a delegate. You start with a UITableView class that presents the table
on-screen and handles user interaction, like a tap to select a row or a
swipe to delete an item. The UITableView depends on one or more other
objects to provide its functionality:
• A table view data source is an object that manages the relationship
between the visual UITableView and its contents. Methods in
the UITableViewDataSource protocol provide the number of sections
and rows in the table, provide titles for headers and footers, and
generate the views for each cell. The data source also has methods
to handle the insertion, deletion, or reordering of table rows. Most
of these features are optional: the only required methods are table-
View:numberOfRowsInSection: and tableView:cellForRowAtIndexPath:.
• A table view delegate allows the host application a greater level
of control over the table’s visual appearance and behavior. An
SETTING UP TABLE-BASED NAVIGATION 88
object implementing the UITableViewDelegate protocol is notified
of various user actions like the beginning and end of row selection
or editing. Other methods allow the delegate to provide customized
views for headers and footers and to specify nondefault
cell heights. The idea of Cocoa delegates was introduced in Section
3.6, Customizing Behavior with Delegation, on page 52, and
in the previous chapter’s Section 4.6, Creating the New View Controller,
on page 75, we used a UITextFieldDelegate to customize keyboard
behavior on a text field.
To use a table in your application, you will create a UITableView, typically
in Interface Builder, and connect it to objects that implement
the data source and delegate protocols. Often, the view controller that
manages the table view will also serve as both the data source and
the delegate. By making the view controller the data source for the
table, you’ll be required to implement tableView:numberOfRowsInSection:
and tableView:cellForRowAtIndexPath: from UITableViewDataSource, and at
a minimum you will usually also implement UITableViewDelegate’s table-
View:didSelectRowAtIndexPath: to handle the user tapping one of the table
rows.
5.2 Setting Up Table-Based Navigation
The UITableView is used as a navigation metaphor throughout the iPhone
OS. In Mail, you use tables to select an account, then a mailbox within
that account, and then a message within that mailbox. For each of
these steps, the data is presented as a table, and selecting a row navigates
to a new view, drilling down either to another table or to a view
that shows the contents of one message. To standardize this kind of
behavior across applications, Xcode provides a Navigation-based Application
project template that uses a table for its first view. We’ll use this
template to learn about tables and navigation in this chapter.
In Xcode, select File > New Project, and choose the Navigation-based
Application template. Make sure the checkbox labeled “Use Core Data
for storage” is not selected.
Name the project MovieTable, and Xcode will set you up with a project
containing two classes (MovieTableAppDelegate and RootViewController),
as well as two nibs (MainWindow.xib and RootViewController.xib).
This is a somewhat more advanced project template than you’ve seen
before, and it helps to understand how the pieces go together. Open
MODELING TABLE DATA 89
Figure 5.2: Navigation objects in the MainWindow.xib file
MainWindow.xib with Interface Builder, and switch the view mode from
icons to list (the middle button of the view mode control). By expanding
the tree structure, you’ll see the arrangement shown in Figure 5.2. The
nib has a navigation controller, an object that we’ll use in code to navigate
forward and backward between views. The navigation controller
has two children: a navigation bar, which you’ll recognize as the bar at
the top of navigation screens (where it usually hosts buttons like Back
or Edit), and the RootViewController, which has a navigation item object.
That’s all well and good for navigating, but where’s the table? If you
inspect the RootViewController, you will see that it gets its view from
RootViewController.xib. Open that nib and look: its default contents are
a single UITableView object. The take-away for now is that a navigation
application has this UINavigationController class that’s responsible
for navigation, which is a parent of the RootViewController, which is the
view controller for the first view the user sees, which is a UITableView.
5.3 Modeling Table Data
Because the RootViewController owns the table view, let’s take a look
at the class. In its implementation file, RootViewController.m, you’ll see
default implementations for some of the table data source and table
delegate methods, three of which are uncommented: numberOfSectionsInTableView:,
tableView:numberOfRowsInSection:, and tableView:cellForRowMODELING
TABLE DATA 90
AtIndexPath. If you look at RootViewController.xib with Interface Builder,
you’ll find that the table’s dataSource and delegate outlets are connected
to File’s Owner. The net result is that this table is wired up and ready
to run; the table expects the RootViewController to serve as its delegate
and data source, and the class provides the minimum implementation
of those protocols to run the application. The protocols are not
explicitly declared in RootViewController.h, because the controller subclasses
UITableViewController, whose declaration includes the two protocols.
Keep in mind that if you ever use some other view controller with
a table, you’ll have to add
to the @interface in the header file to declare that you implement these
protocols.
The default implementation provides for a table that has one section
with zero rows. It also provides logic for creating cell views in code,
but with zero rows, that code will never be used. So, the first thing we
need to do is to implement the section- and row-count methods in a
nontrivial way. This means we need to develop a model for the table
data; with the RootViewController mediating between the view and this
model, we’ll have implemented the classic Model View Controller design
pattern.
A table model does not have to be anything fancy; it’s not a class unto
itself as it is in other languages. For a one-section table, it’s practical
to just use an NSArray, which contains the objects the table represents.
The array’s length gives you the number of rows, and the contents for
a given cell can be looked up with the array’s objectAtIndex: method.
At the beginning of this chapter, we said that we would reuse the previous
chapter’s Movie class as the data for our table. In Groups & Files,
Ctrl+click or right-click the Classes folder, and choose Add > Existing
Files. Navigate to the previous Movie project,1 select its Classes folder,
use the Command key (D) to select Movie.h and Movie.m, and click the
Add button. In the next dialog box, make sure the checkbox for “Copy
items into destination group’s folder (if needed)” is selected, and click
Add again to copy the files into this project.
Also add #import "Movie.h" to RootViewController.h, since we’ll be using the
Movie class in our view controller.
1. In the book’s downloadable code, for example, this would be ViewControllers/Movie02.
MODELING TABLE DATA 91
Next, since we’re going to be offering an editable list of Movies, we’ll
want to use an array that we can add to and remove from. So, declare
the instance variable NSMutableArray *moviesArray; in the @interface block
of RootViewController.h. This array needs to be initialized, and we’ll want
to provide some data for our table (we’ll allow the user to add their
own data later), so uncomment the provided viewDidLoad method in
RootViewController.m, and add the highlighted code to create one Movie
and add it to the array:
Download TableViews/MovieTable01/Classes/RootViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
moviesArray = [[NSMutableArray alloc] init];
Movie *aMovie = [[Movie alloc] init];
aMovie.title = @"Plaything Anecdote" ;
aMovie.boxOfficeGross = [NSNumber numberWithInt: 191796233];
aMovie.summary =
@"Did you ever think your dolls were really alive? Well, they are." ;
[moviesArray addObject: aMovie];
[aMovie release];
}
Now that our model has some genuine data, we need to update the
UITableViewDataSource methods to get that data to the on-screen UITable-
View. The default numberOfSectionsInTableView: returns 1, which is fine
as is. However, the tableView:numberOfRowsInSection: returns 0, which is
wrong. We want it to return the length of the array:
Download TableViews/MovieTable01/Classes/RootViewController.m
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [moviesArray count];
}
That will tell the table view that the one section has one row. As a result,
when the application runs, it’ll call the tableView:cellForRowAtIndexPath:
method to get a UITableViewCell for that one row. The template provides
a default implementation that creates a cell in code; we just have to
customize that cell, immediately after the provided comment // Configure
the cell and before return cell;.
What we need to do is to figure out which member of the array—there’s
only one now, but there will be many later—we want to use for the
cell’s contents. The key is to use the indexPath variable. An NSIndexPath
is an object that specifies a path through a tree structure as a set of
TABLE CELLS 92
zero-based integer indexes. On iPhone OS, this class is extended with
properties specifically for use with UITableViews: section and row. In other
words, any time you handle tableView:cellForRowAtIndexPath:, the section
and row of the cell being requested are indicated as indexPath.section
and indexPath.row.
So, right before return cell; in the provided implementation, add the following
code:
Download TableViews/MovieTable01/Classes/RootViewController.m
Movie *aMovie = [moviesArray objectAtIndex:indexPath.row];
cell.textLabel.text = aMovie.title;
The first gets the member of moviesArray that corresponds to the selected
row, which is the value of indexPath.row. Then we just need to present
the Movie’s title in the cell. The UITableViewCell provides two UILabels as
properties: textLabel and detailTextLabel. For this simple case, we set the
textLabel’s text to the movie title.
That’s all that’s necessary for a basic table. Build and Go. You’ll see the
one-row table shown in Figure 5.3, on the next page.
5.4 Table Cells
Thus far, we’ve provided enough of a data source implementation to get
a minimal table on-screen, but there’s a lot more we can do with this
table, starting with the table cells. After all, while our Movie class has
three member properties, we’re showing only one of them in the table.
Let’s look into getting more use from our cells.
Cell Styles
The provided implementation of tableView:cellForRowAtIndexPath: creates
a UITableViewCell object called cell that we customize before returning
it to the caller. The default cell has three visual properties that can be
used to put our data in the cell: textLabel, detailTextLabel, and imageView.
In this example, we set the text of the textLabel to get the basic, default
appearance. If the Movie class had an NSImage member (like a screenshot
or DVD box art), then we could set the imageView’s image property
to make the image appear on the left side of the cell.
TABLE CELLS 93
Figure 5.3: A basic UITableView
To make use of the detailTextLabel, we need to choose a different cell
style. The idea of the style is new in iPhone 3.0, and four styles are
provided:
• UITableViewCellStyleDefault: Presents the textLabel single block of
left-aligned black text. The detailTextLabel is absent. This default
is identical in appearance to table cells in iPhone 2.x.
• UITableViewCellStyleSubtitle: Presents a large left-aligned black text-
Label and a second line for a smaller, gray detailTextLabel below it,
similar to the iPod or Music application.
• UITableViewCellStyleValue1: Presents a large left-aligned black textLabel
on the left and a slightly smaller right-aligned detailTextLabel
on the right in blue. This layout resembles cells in the Settings
application and is intended only for use in group-style tables.
TABLE CELLS 94
Figure 5.4: UITableViews displaying the four provided UITableViewCell-
Styles, in plain and grouped mode
• UITableViewCellStyleValue2: Presents a small right-aligned blue text-
Label on the left and a small left-aligned black detailTextLabel on the
right, similar to the Contacts application. Again, this button-like
style is appropriate for use only in grouped tables.
In Figure 5.4, we can see these four styles in a modified version of
the sample application. We’ve changed the contents of the cells based
on their style, because some of the layouts are inappropriate for large
strings, particularly UITableViewCellStyleValue2, whose left-side label will
truncate after about ten characters. Since the “value” styles are meant
for a button-like presentation in grouped tables, the screenshot on the
right of the figure puts each cell in its own section, while the left screenshot
is a one-section table with four rows.
TABLE CELLS 95
Use Styles for Table Cells, Not CGRect
Prior to iPhone SDK 3.0, UITableViewCell’s designated initializer
was initWithFrame:reuseIdentifier:, which took a CGRect (usually
the constant CGRectZero, since it wasn’t actually used) for
the frame argument. The navigation-application template provided
a call to this initializer, as did all other table code. However,
in iPhone SDK 3.0, this initializer is deprecated in favor of
initWithStyle:reuseIdentifier:, which takes one of the style constants
instead of the CGRect. It’s trivially easy to convert old code to
the new standard by just switching to the new call and using
the UITableViewCellStyleDefault style.
The provided styles offer some flexibility in presenting your data in the
space afforded by a list on a small device like the iPhone. If none of
these styles, with or without the optional imageView, suits your needs,
then continue to Section 5.7, Custom Table View Cells, on page 105, in
which we’ll look at how to create custom cell layouts.
Cell Reuse
Along with a style, the initializer for a UITableViewCell takes a reuseIdentifier
string. Understanding how this object is used is critical to creating
tables that perform as expected. Fortunately, the default implementation
of tableView:cellForRowAtIndexPath: shows us what this property does
and how it is to be used.
A UITableView caches cells for later reuse, which improves performance
by recycling cells rather than repeatedly creating them anew. When a
cell completely scrolls off the top or bottom of the screen, it becomes
available for reuse. So, when you need to create a table cell in table-
View:cellForRowAtIndexPath:, you first try to retrieve an existing cell from
the table’s cache. If it works, you just reset that cell’s contents; if it
fails, presumably because no cached cells are available, only then do
you create a new cell.2
2. This means that dequeuing occurs only when there is enough data for the table to
fill the screen and the user has scrolled far enough for one or more cells to go entirely
off-screen.
EDITING TABLES 96
Here’s the default implementation in tableView:cellForRowAtIndexPath::3
Download TableViews/MovieTable01/Classes/RootViewController.m
Line 1 static NSString *CellIdentifier = @"Cell" ;
2 UITableViewCell *cell =
3 [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
4 if (cell == nil) {
5 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
6 reuseIdentifier:CellIdentifier] autorelease];
7 }
Line 1 creates a cell identifier, a string that indicates the kind of cell
we want. The idea here is that if you use different styles of cells in the
same table (either default styles or layouts of your own creation), you
will need to distinguish them in the table’s cache so you get back the
style of cell you need. In the default case, you use only one style, so any
arbitrary string like "Cell" will suffice. Next, lines 2–3 attempt to dequeue
a cell, that is to say, to retrieve a cell from the table’s cache, passing in
the identifier to indicate what kind of cell is needed. If this fails, then a
new cell is allocated and initialized.
5.5 Editing Tables
So now, we’ve covered how to provide table contents and gain some
control over how the contents of a cell are presented. The next step is
to make the table editable. What this really means is that we want to
make the table serve as an interface for editing the underlying model.
When we delete a row in the table, we want to delete the object from
the model, and when we add an item to the model, we want the table
updated to reflect that.
Let’s start with deletes, which are easier. In fact, the commented-out
code provided by the navigation-application template includes the
basics of what we need to provide deletion. Start with tableView:canEdit-
RowAtIndexPath:. The default implementation (and the default behavior,
if this UITableViewDataSource method is not implemented at all) is to not
permit editing of any row. Uncomment the default implementation, and
change it to return YES;. When you do this, you’ll find that you can swipe
horizontally on table rows to bring up a Delete button.
3. We’ve reformatted the default code to fit the layout of this book.
EDITING TABLES 97
To implement the delete, we need to implement tableView:commitEditing-
Style:forRowAtIndexPath:. The commented-out implementation has an ifthen
block for handling cases where the editing style is UITableView-
CellEditingStyleDelete and UITableViewCellEditingStyleInsert. We need to support
the former only. To perform a delete, we need to do two things:
remove the indicated object from the moviesArray model, and then refresh
the on-screen UITableView. For the latter, UITableView provides the
method deleteRowsAtIndexPaths:withRowAnimation:, which is exactly what
we need. Add the highlighted line to the default implementation, as
shown here, and delete the else block for UITableViewCellEditingStyleInsert:
Download TableViews/MovieTable01/Classes/RootViewController.m
- (void)tableView:(UITableView *)tableView
commitEditingStyle: (UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source.
[moviesArray removeObjectAtIndex: indexPath.row];
[tableView deleteRowsAtIndexPaths:
[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
This gives us swipe-to-delete behavior, but some users don’t even know
it exists. Fortunately, since we’re a navigation app, we have a navigation
bar at the top of the screen that is well suited to hosting an Edit button.
As in other apps, its default behavior when active is to add an “unlock
to delete” button to the left side of every table row that allows editing,
which brings up the right-side Delete button when tapped.
In the viewDidLoad method you uncommented, you might have noticed
the following commented-out code:
Download TableViews/MovieTable01/Classes/RootViewController.m
// Uncomment the following line to display an Edit button in the
// navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
You might recall from Section 5.2, Setting Up Table-Based Navigation,
on page 88 that in MainView.xib, the RootViewController came set up with
UINavigationItem as a child element. That represents the blue bar above
the table, typically used for forward-back navigation and for editing
tables. It has two properties for setting buttons in the bar: leftBarButtonItem
and rightBarButtonItem. Then, on the right side of this assignment,
notice the reference to self.editButtonItem. Every UIViewController
NAVIGATING WITH TABLES 98
supports this editButtonItem property, which returns a UIBarButtonItem
that calls the view controller’s setEditing:animated: method and toggles
its state between Edit and Done.
The commented-out line is almost what we want, but let’s put the Edit
button on the left, so we can leave the right side for an Add button that
we’ll create later. So, here’s the line you’ll need in viewDidLoad:
Download TableViews/MovieTable01/Classes/RootViewController.m
self.navigationItem.leftBarButtonItem = self.editButtonItem;
Once you Build and Go, you should now be able to tap the Edit button
and bring up the unlock-to-delete button for all the rows. In Figure 5.5,
on the next page, we can see the table in editing mode (with some more
sample data to fill out its rows).
5.6 Navigating with Tables
Our next task is to allow the user to add a table row. In the previous
chapter, we developed a MovieEditorViewController, and that’s perfectly
well suited to entering the fields of a new Movie object or editing an
existing one. And once created, it would be simple enough to add the
new Movie object to the model and update the table.
So, where do we put the editor? In the previous chapter, we used the
UIViewController method presentModalViewController:animated: to slide in
the editor. In this case, we’re going to learn something new: how to
use the navigation objects at our disposal. We created the project as a
navigation-based application in part because it gave us a good starting
point for our table, and navigation also turns out to be a good idiom for
switching between our viewing and editing tasks.
Navigation on the iPhone uses a “drill-down” metaphor that you are
probably familiar with from the Mail, iPod/Music, and Settings applications.
In the SDK, this is managed by a UINavigationController, which
maintains the navigation state as a stack of view controllers. Every time
you drill down, you push a new UIViewController onto the stack. When
you go back, you pop the current view controller off the stack, returning
to the previous view. The navigation is handled in code, independent of
how it’s represented on-screen: whether you navigate by tapping rows
in a table or buttons in the navigation bar, the underlying stack management
is the same.
NAVIGATING WITH TABLES 99
Figure 5.5: Using the default editButtonItem to delete rows from a UITable-
View
Adding the MovieEditorViewController
To try this, let’s get to the MovieEditorViewController by means of the navigation
API. In fact, we’ll use it for two purposes: to edit items already
in the table and to create new items.
As with the Movie class, you’ll need to copy the MovieEditorViewController.
h and MovieEditorViewcontroller.m classes to your project’s Classes
folder and then add those copies to the Xcode project. Also copy over
the MovieEditorViewController.xib (with Add > Existing Files as before) to
the project’s Resources group. In the earlier examples, this editor view
was presented modally and took up the whole screen. In this application,
it’s part of the navigation, and therefore the navigation bar will
take up some space above the view. Fortunately, Interface Builder lets
us simulate a navigation bar to make sure everything still fits in the
NAVIGATING WITH TABLES 100
view. Open the nib in IB, select the view, and bring up its Property
inspector (D1). Under Simulated Interface Elements, set Top Bar to
Navigation Bar to see how the view will look as part of the navigation.
In this case, the Done button won’t be pushed off-screen, but you might
want to adjust its position to get it inside IB’s dashed margin.
To bring up the movie editor, our RootViewController needs to push an
instance of the MovieEditorViewController on to the navigation stack. We
could create the view controller in code, but since we only ever need one
instance, it makes sense to create it in Interface Builder. The first step,
then, is to create an IBOutlet in RootViewController.h. Add an instance variable
MovieEditorViewController* movieEditor; inside the @interface’s curlybrace
block, and then declare the property as an outlet after the close
brace:
Download TableViews/MovieTable01/Classes/RootViewController.h
@property (nonatomic, retain) IBOutlet MovieEditorViewController *movieEditor;
As usual, you’ll need to @synthesize this property in the .m file. Also,
remember to put #import "MovieEditorViewController.h" in the header.
Now you’re ready to create an instance of MovieEditorViewController in
Interface Builder. Open RootViewController.xib with IB, and drag a UIView-
Controller from the Library into the nib document window. Select this
view controller, and use the Identity inspector (D4) to set its class to
MovieEditorViewController. The last step is to connect this object to the
outlet you just created. Ctrl+click or right-click File’s Owner (or show its
Connections inspector with D2), and drag a connection from movieEditor
to the view controller object you just created. We’re done with IB for
now, so save the file.
Editing an Existing Table Item
Let’s start by using the MovieEditorViewController to edit an item in the
table. When the user selects a row, we’ll navigate to the editor and load
the current state of the selected Movie object into the editor.
The first thing we need to do is to react to the selection event. The
UITableViewDelegate gets this event in the delegate method tableView:did-
SelectRowAtIndexPath:. The navigation-application template provides a
commented-out version of this method in RootViewController, though its
sample code creates a new view controller programatically. We don’t
need to do that, since we already have the next view controller. It’s the
movieEditor that we just set up in Interface Builder. So, we just need to
set up that view controller and navigate to it.
NAVIGATING WITH TABLES 101
Declare an instance variable of type Movie* named editingMovie in the
header file. It remembers which Movie object is being edited, so we’ll
know what to update in the table when we navigate to the table. Once
you’ve done that, the steps here are pretty simple. Remember what
movie we’re editing, tell the MovieEditorViewController what movie it’s editing,
and navigate to that view controller with the UINavigationController’s
pushViewController:animated: method.
Download TableViews/MovieTable01/Classes/RootViewController.m
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
editingMovie = [moviesArray objectAtIndex:indexPath.row];
movieEditor.movie = editingMovie;
[self.navigationController pushViewController:movieEditor animated:YES];
}
What’s interesting about the last step is how we get a reference to the
navigation controller. . . remember, we haven’t defined an ivar or property
for it; in fact, the navigation controller was created for us in Main-
Window.xib, and we haven’t touched it with IB. The neat trick is the navigationController
property defined by the UIViewController class and therefore
inherited by RootViewController. This property (also callable as an
instance method) looks through the object hierarchy to find a parent or
ancestor object that is a UINavigationController. Thanks to this method,
you never need to explicitly make connections to your navigation controller.
Your root view controller and any view controllers it pushes onto
the navigation stack can get to the navigation controller with this property,
using it to navigate forward or back or to update the on-screen
navigation bar.
This is all we need to do to the movie editor view; now we need a way to
get back from the editor to the root. MovieEditorViewController has a done
method that’s connected in IB to the Done button,4 but its implementation
needs to be updated. Instead of dismissing itself as a modal view
controller, it needs to navigate back to the previous view controller:
Download TableViews/MovieTable01/Classes/MovieEditorViewController.m
- (IBAction)done {
[self.navigationController popViewControllerAnimated:YES];
}
4. If we didn’t already have a Done button in the view, it would be more typical to set up
a Done or Back button in the navigation bar. The navigation in the example in Chapter 8,
File I/O, on page 138 will work like this.
NAVIGATING WITH TABLES 102
As you can see, the MovieEditorViewController also can use the inherited
navigationController property to get the UINavigationController.
This will navigate to and from the movie editor; the only task left to
attend to is to update the table when we return from an edit. The
RootViewController will get the viewWillAppear: callback when we navigate
back to it, so we can use that as a signal to update the table view:
Download TableViews/MovieTable01/Classes/RootViewController.m
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// update table view if a movie was edited
if (editingMovie) {
NSIndexPath *updatedPath = [NSIndexPath
indexPathForRow: [moviesArray indexOfObject: editingMovie]
inSection: 0];
NSArray *updatedPaths = [NSArray arrayWithObject:updatedPath];
[self.tableView reloadRowsAtIndexPaths:updatedPaths
withRowAnimation:NO];
editingMovie = nil;
}
}
We gate our update logic with a check to see whether a movie is being
edited, since this method will also be called at other times (at startup,
for example). If we are returning from an edit, we need to identify the
one table row being updated. We can figure this out by getting the array
index that corresponds to editingMovie, constructing an NSIndexPath that
goes to that row in section 0 of the table, and pass the path to the table
view’s reloadRowsAtIndexPaths:withAnimation: method.
Adding an Item to the Table
Another thing we’d like to support is the ability to add new items to the
table. We can actually make this a special case of editing. When the
user taps an Add button, we quietly add an empty Movie to the table
model, insert a table row, and navigate to the editor.
Previously, we used the navigation bar’s leftBarButtonItem for the provided
editButtonItem, so let’s put the Add button on the right side of the
navigation bar. We don’t inherit an Add button from UIViewController like
we did with the Edit button, so we’ll create one ourselves.
NAVIGATING WITH TABLES 103
First, go to RootViewController.h, and set up an IBAction to handle an event
from the button we’re about to create:
Download TableViews/MovieTable01/Classes/RootViewController.h
-(IBAction) handleAddTapped;
Now, since we need to work with the navigation objects that Xcode
created for us, we’ll use Interface Builder to open the MainWindow.xib
file, where they’re defined. Switch the view mode in the nib document
window to list or column view, and double-click the Navigation Controller
object. This will bring up a window with the navigation bar at
the top and a view placeholder at the bottom that says it’s loaded from
RootViewController. You’ll notice that the Edit button is absent from the
left side of the navigation bar, because we add it with code at runtime.
Go to the Library, and find the icon for the Bar Button Item. This is
different from the usual Round Rect Button, so make sure the object
you’ve found lists its class as UIBarButtonItem. Drag the bar button to the
right side of the navigation bar, where you’ll find it automatically finds
its way to a highlighted landing spot, making it the navigation bar’s
rightBarButtonItem. Select the bar button, bring up the Identity inspector
(D1), and change its identifier to Add. This will change its appearance
to a simple plus sign (+).
The next step is to connect this button to the handleAddTapped method.
This is a little different from the connections you’ve made thus far. First,
when you bring up the button’s Connections inspector (D2), you won’t
see the usual battery of touch events like Touch Up Inside. Instead,
there’s a single Sent Action called selector. This is because the UIBar-
ButtonItem has a different object hierarchy than regular buttons and
doesn’t have UIControl, UIView, and UIResponder as superclasses. Instead,
this object has properties called target and selector; when the bar button
is tapped, the method named by selector is called on the target object.
You could set both of those properties in code; since we’re already here
in Interface Builder, let’s set it up here.
To set the selector and target, we drag the selector action from the Connections
inspector to one of the other objects in the nib. This time,
however, we don’t drag it to the File’s Owner. Since this is the MainWindow.
xib, the File’s Owner proxy object points to a generic UIApplication.
The handleAddTapped method that we want the button to call is defined
in the RootViewController class, so we drag the connection to the Root
View Controller object in the nib window, as shown in Figure 5.6, on
the next page. When you release the mouse button at the end of the
NAVIGATING WITH TABLES 104
Figure 5.6: Connecting a UIBarButtonItem’s selector to the RootViewController
drag, the names of the target’s IBAction methods will appear, and you’ll
select the only one: handleAddTapped.
With the connection made, save in IB and return to Xcode. Now we can
implement the handleAddTapped method that will be called when the
user taps the Add button:
Download TableViews/MovieTable01/Classes/RootViewController.m
-(IBAction) handleAddTapped {
Movie *newMovie = [[Movie alloc] init];
editingMovie = newMovie;
movieEditor.movie = editingMovie;
[self.navigationController pushViewController:movieEditor animated:YES];
// update UITableView (in background) with new member
[moviesArray addObject: newMovie];
NSIndexPath *newMoviePath =
[NSIndexPath indexPathForRow: [moviesArray count]-1 inSection:0];
NSArray *newMoviePaths = [NSArray arrayWithObject:newMoviePath];
[self.tableView insertRowsAtIndexPaths:newMoviePaths withRowAnimation:NO];
[newMovie release];
}
CUSTOM TABLE VIEW CELLS 105
This method starts by creating an empty Movie object, setting it as the
editingMovie, and navigating to the MovieEditorViewController, much like
the code to edit an existing Movie did. What’s different is that after navigating,
it does cleanup work on the table view (while the table is out
of sight) by adding the new object to the model array and then calling
insertRowsAtIndexPaths:withRowAnimation: to update the table to reflect
the new state of the model. The inserted Movie has blank fields, but
when the user returns from the editor, the object will be updated in
viewWillAppear:, just like when an existing item is edited.
Let’s review. We used the navigation-application template to set up an
application with a table view, which we backed with a model (a simple
NSMutableArray) to provide a list of Movie objects. After looking at the
various table cell styles, we added the ability to delete from the table
either with horizontal swipes (by implementing tableView:canEditRowAt-
IndexPath:), or with the Edit button (by adding the default editButtonItem
and implementing tableView:commitEditingStyle:forRowAtIndexPath:). Then
we looked at how to access the UINavigationControl to navigate between
view controllers and used the MovieEditorViewController to edit a Movie
indicated by a selected row in the table and then to edit a new Movie in
response to the tap of an Add bar button.
5.7 Custom Table View Cells
Back in Section 5.4, Cell Styles, on page 92, we looked at the four cell
styles provided by iPhone OS. Although they suit a wide range of uses,
sometimes you might want something else. If your GUI uses a unique
color theme, the default black or blue text on white cells might not suit
you. If you need to populate more than two labels, then none of the
available styles will work for you.
It is possible, with a little work, to custom design your own table cell
in Interface Builder and have your table use this design instead of the
built-in styles. In this section, we’ll use this technique to create a table
that shows all three of the Movie fields.5
5. Because we will change so much in the project to use custom table cells, the book’s
downloadable code examples have split this exercise into a separate project. The previous
material is represented by MovieTable01, and the custom-cell project is MovieTable02.
CUSTOM TABLE VIEW CELLS 106
Designing a Custom Table Cell
Every UITableViewCell has a contentView, so it’s possible to programmatically
create subviews and add them to this view; some Apple sample
code does this. The problem is that you then have to customize the location,
font, size, and other properties of each subview with code, without
a visual editor. The second approach is to create a UITableViewCell in a
nib file, add the subviews visually, and load that nib when the table
needs a new cell. This is what we’ll do.
In Xcode, select the Resources group and use File > New File to create a
new file, choose User Interface from the iPhone OS section, and create
an empty nib file called MovieTableCell.xib. Open this file in Interface
Builder. The document will contain just the usual two proxy objects:
File’s Owner and First Responder. From the Library, drag a table view
cell into the nib window. Double-click to edit the object, which will
open a small window the size of a typical table view cell, with a gray
area designated as the content view.
The content view is really just an IB visual artifact, a placeholder for the
created-at-runtime view that contains all our subviews, so we’ll place
our UI elements directly on top of it. The Movie class has three fields,
so we’ll use three labels to put those fields in a single cell, adjusting
the font, color, sizing, and layout appropriate to the items’ respective
importance. Drag three UILabels from the library into the cell, using
the positioning handles and the Attributes inspector (D1) to customize
their location, bounds, color, and font. For the samples in the book’s
downloadable example code, here’s what we used:
• Movie Title: Georgia 17-point font, yellow text, left-aligned near the
left side of the cell, toward the top
• Box Office Gross: Helvetica 17-point font, green text, right-aligned
near the right edge
• Summary: Helvetica 10-point font, light blue text, along the entire
bottom of the cell
Our cell design in Interface Builder is shown in Figure 5.7, on the next
page. We used lighter colors because we plan to use a black background
for the table, although this makes the colors harder to see against the
background of the gray Content View placeholder. You’ll also notice that
we’ve put somewhat plausible data in each of the fields to get a sense of
how much space each needs and what they’ll look like with real data.
Save MovieTableCell.xib, then open RootViewController.xib, find the table,
CUSTOM TABLE VIEW CELLS 107
Figure 5.7: Designing a custom UITableViewCell in Interface Builder
and use the Attributes inspector to set its background color to black.
We have to do this because parts of the table cell are transparent and
we might not have enough cells to fill the table, and we want empty
parts of the table to have the same background as populated cells.
Loading and Using a Custom Table Cell
We have a custom table cell, so how do we use it in the table? If you
think about it, we really need many table cells. The default behavior
of the table is to create a new cell in code each time we try to fail to
dequeue a reusable cell from the table’s cache. If we’re going to use the
cell from this nib, then we have to load a new custom cell each time we
would have created a cell with code.
There’s an interesting trick to how we do this. We can manually load the
nib in code and find the cell within the nib. To do this, create an IBOutlet
property in RootViewController.h to hold onto a UITableViewCell loaded from
the nib.
Download TableViews/MovieTable02/Classes/RootViewController.h
@interface RootViewController : UITableViewController {
// ... other ivars omitted here for space
UITableViewCell *nibLoadedCell;
}
@property (nonatomic, retain) IBOutlet MovieEditorViewController *movieEditor;
@property (nonatomic, retain) IBOutlet UITableViewCell *nibLoadedCell;
-(IBAction) handleAddTapped;
@end
Now, go back to editing MovieTableCell.xib in Interface Builder. Select
File’s Owner, bring up its Identity inspector (D4), and change its class
to RootViewController. Having done this, you should be able to switch to
the Connections inspector (D2) and connect the nibLoadedCell outlet to
CUSTOM TABLE VIEW CELLS 108
The Secret of File’s Owner
The technique of loading a custom table cell from a nib
should also demystify the nature of File’s Owner in Interface
Builder. In IB, File’s Owner is a proxy object that refers to whatever
object “owns” the nib file. You can set the class of File’s
Owner in order to access that class’ outlets and actions, but
all you’re really doing is making an implicit assertion that some
object of that class will be the one that owns the nib when
it’s loaded. Here, you see the other side of that relationship:
loadNibNamed:owner:options: loads the nib, specifying an owner
object. Any connections to File’s Owner get connected to or
from this object as part of loading the nib.
the cell object in the nib window. While you’re in IB, select the table
cell, bring up its Attributes inspector, and change the identifier (the
first field) to Cell. You may recognize this as the “reuse identifier” string
we used in Section 5.4, Cell Reuse, on page 95.
Now for the surprising part. In RootViewController.m, go to the table-
View:cellForRowAtIndexPath: method, and rewrite the if (cell==nil) block as
follows:
Download TableViews/MovieTable02/Classes/RootViewController.m
Line 1 if (cell == nil) {
2 [[NSBundle mainBundle] loadNibNamed:@"MovieTableCell"
3 owner:self options:NULL];
4 cell = nibLoadedCell;
5 }
This eliminates the programmatic creation of the table cell, but the
means by which cell gets assigned is not necessarily obvious, because
the most important step is implicit. On line 2 is where we load the MovieTableCell
nib. This returns an NSArray of the nib contents, which we
could iterate over to find the table cell object. But we don’t have to,
because we declared an outlet from that cell to the nibLoadedCell property.
The outlets are connected as a consequence of loading the nib,
meaning that when loadNibNamed:owner:options: returns, the nibLoaded-
Cell has a reference to the custom cell loaded from the nib, which we
can then assign to the local variable, cell, on line 4.
SORTING TABLE DATA 109
Assigning Values in a Custom Table Cell
Each time a new cell is needed, loadNibNamed:owner:options: will be
called again, creating a new cell object in memory. So, at the end of
the if, we have a cell (either dequeued from the table or loaded from
the nib) that we need to customize with the values of a Movie from
the model. But with a custom cell, we can no longer use the textLabel or
detailTextLabel properties. Instead, we need a way to access the subviews
we added in Interface Builder.
One option would be to create a custom UITableViewCell subclass, declare
and connect outlets in that class, and then cast the cell to that
class when loaded. The only downside is that there are lots more classes
to write, one for every kind of table cell in your application. Here’s a
somewhat more direct technique. Open the cell in Interface Builder,
and select the title label. Open the Attributes inspector, and scroll down
to the field marked Tag. The tag is a simple, unique integer to identify
one view within a view hierarchy. Use the Attributes inspector to set
the title label’s tag to 1, the box office gross label to 2, and the summary
label to 3.
Now, back in tableView:cellForRowAtIndexPath:, you can customize each
label’s text by looking up the label with the cell’s viewWithTag: method.
Download TableViews/MovieTable02/Classes/RootViewController.m
// Configure the cell.
Movie *aMovie = [moviesArray objectAtIndex:indexPath.row];
UILabel *titleLabel = (UILabel*) [cell viewWithTag:1];
titleLabel.text = aMovie.title;
UILabel *boxOfficeLabel = (UILabel*) [cell viewWithTag:2];
boxOfficeLabel.text = [NSString stringWithFormat: @"%d" ,
[aMovie.boxOfficeGross intValue]];
UILabel *summaryLabel = (UILabel*) [cell viewWithTag:3];
summaryLabel.text = aMovie.summary;
return cell;
And now, we’re ready to go. We have a custom cell design in a nib, along
with new table code to load and populate that cell. Build and Go to see
a table like the one shown in Figure 5.8, on the next page.
5.8 Sorting Table Data
Another common task for developers who use tables is to sort the data
in the table. Fortunately, Cocoa and Objective-C give us some unique
advantages and make this an enviably easy task.
SORTING TABLE DATA 110
Figure 5.8: A UITableView with custom-designed cells
To add sortability, we’ll start by adding a sorting control to our user
interface.6 Open MainWindow.xib in Interface Builder, and double-click
the Navigation Controller object to bring up its view. Drag a segmented
control from the Library to the center of the navigation bar. The UISegmentedControl
is a useful control that allows the user to select one of
a small number of preset values. Even though it automatically adjusts
its size for the limited space of the navigation bar, this one won’t have
room for many options, so let’s just use three. Select the segmented
control, and open the Attributes inspector. Set the number of segments
to 3, using the Title field to set the titles of the segments to A-Z, Z-A, and
$ (or whatever monetary symbol makes the most sense for your locale).
6. Once again, we’re making enough changes to merit a separate project in the book’s
downloadable code. The sorting version of the project is MovieTable03.
SORTING TABLE DATA 111
We’ll need to access this segmented control from code, so we’ll need an
outlet to it. In RootViewController.h, declare the instance variable UISegmentedControl*
sortControl; and set up a property for it with the usual
@property and @synthesize statements, as with the other properties you’ve
already created. You’ll also need to declare this method to handle the
event when the user taps the sort control:
Download TableViews/MovieTable03/Classes/RootViewController.h
-(IBAction) handleSortChanged;
In IB, still in MainWindow.xib, you should now be able to connect the
Root View Controller object’s sortControl outlet to the segmented control,
as well as connect the segmented control’s Value Changed event to the
Root View Controller’s handleSortChanged method.
We’ll need to sort the array both in response to the user changing the
segmented control and to other causes: adding or editing an item will
require a re-sort, plus we’ll need to sort the array when the application
first starts up. So, let’s plan on writing a sortMoviesArray method, which
we’ll get to in a minute. We can now implement handleSortChanged
pretty trivially:
Download TableViews/MovieTable03/Classes/RootViewController.m
-(IBAction) handleSortChanged {
[self sortMoviesArray];
[self.tableView reloadData];
}
Whenever the sort type changes, we sort the array and then tell the
UITableView to reload all its data. This could be expensive, but a sort
may well change every row of the table, making the update of individual
rows impractical. We also need to add these two lines of code to the
bottom of viewWillAppear: so that we re-sort and update the table when
the application starts up, when an item is edited, and when an item is
added.
How do we perform the sort? It’s actually pretty easy. The sortMoviesArray
method needs to appear in the implementation before any call to it
(or else you can put the method signature in the header file, although
that exposes it publicly). To perform the sort, we’ll rely on the fact that
the NSArray provides a number of methods to return a sorted copy of an
array, and NSMutableArray offers these as methods to sort the mutable
array itself. Some of these take function pointers or Objective-C selectors,
allowing you to write a custom sorting function. But the easiest
option is to use sort descriptors.
SORTING TABLE DATA 112
The NSSortDescriptor is a class that describes a sorting criteria, consisting
simply of a key and a BOOL to indicate whether the sort is ascending.
The way this works is to use Key-Value Coding to access the field to sort
by. The key is a string that defines a key path, which is a dot-separated
path of “getable” properties of an object. For each step of the path, the
path segment is retrieved by attempting to access a property, accessor
method, or instance variable with the key name. The sort descriptor
then uses a default selector defined for many Cocoa objects, compare:,
to actually perform the sort.7
Our Movie objects are very simple, having three properties. To sort
alphabetically by the title, we just create a sort descriptor whose key
is title, the name of the property.
With that description in mind, look how simple it is to implement all
three of our sorts:
Download TableViews/MovieTable03/Classes/RootViewController.m
-(void) sortMoviesArray {
NSSortDescriptor *sorter;
switch (sortControl.selectedSegmentIndex) {
case 0: // sort alpha ascending
sorter = [[NSSortDescriptor alloc]
initWithKey:@"title" ascending:YES];
break;
case 1: // sort alpha descending
sorter = [[NSSortDescriptor alloc]
initWithKey:@"title" ascending:NO];
break;
case 2:
default: // sort $$ ascending
sorter = [[NSSortDescriptor alloc]
initWithKey:@"boxOfficeGross" ascending:YES];
break;
}
NSArray *sortDescriptors = [NSArray arrayWithObject: sorter];
[moviesArray sortUsingDescriptors:sortDescriptors];
[sorter release];
}
This implementation sets up a single NSSortDescriptor appropriate to the
selected sort type and puts it into an array for use by NSMutableArray’s
sortUsingDescriptors:. The reason this takes an array is that you could
7. If your properties were custom classes that didn’t respond to compare:, you could
change the sorting selector, but you’ll usually be sorting Cocoa classes like NSString and
NSNumber, which have sensible implementations of compare:.
SORTING TABLE DATA 113
Figure 5.9: Sorting the table alphabetically by title
provide multiple descriptors to perform secondary sorts; two objects
determined to be equal by the first sort descriptor would then be sorted
by the second descriptor in the array, and so on.
With these changes, our sorting behavior is ready to go. In fact, you
will see it as soon as you launch the application, since viewWillAppear:
makes a call to sortMoviesArray at startup.

Monday, January 24, 2011

Killer Beats iPhone App Review

From the team that brought you top-10 muItalicsic Apps Killer Riffs & This Day In Music, Killer Beats is the brand new App for all drummers out there!

This app is pretty useful, I gave it to my drummer to test and he got pretty into it, there is a good selection of songs by popular artists and he says the tabs are right on. The file is large but not huge, and the interface is well designed...

Rating 4 stars

£2.99
Category: Music
Updated: 16 December 2010
Current Version: 1.1
1.1 (iOS 4.0 Tested)
Size: 161 MB
Language: English
Developer: Musicroom.com
© Music Sales Limited
Rated 4+

Music iPhone App Development

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.