You are previewing Head First iPhone Development.

Head First iPhone Development

Cover of Head First iPhone Development by Dan Pilone... Published by O'Reilly Media, Inc.
  1. Copyright
  2. Advance Praise for Head First iPhone Development
  3. The Authors
  4. how to use this book: Intro
    1. Who is this book for?
      1. Who should probably back away from this book?
    2. We know what you're thinking.
    3. And we know what your brain is thinking.
    4. Metacognition: thinking about thinking
      1. Here's what WE did:
    5. Here's what YOU can do to bend your brain into submission
    6. Read me
      1. System requirements
    7. The technical review team
    8. Acknowledgments
    9. Safari® Books Online
  5. 1. getting started: Going mobile
    1. 1.1. There's a lot of buzz and a lot of money tied up in the App Store...
    2. 1.2. Mobile applications aren't just ported desktop apps
      1. 1.2.1. iPhone apps are not small desktop apps
    3. 1.3. Anatomy of an iPhone app
      1. 1.3.1. First we have one or more views...
      2. 1.3.2. ...then the code that makes the views work...
      3. 1.3.3. ...and any other resources, all packaged into your application.
    4. 1.4. Mike can't make a decision
    5. 1.5. Make a good first impression
    6. 1.6. It all starts with the iPhone SDK
    7. 1.7. Xcode includes app templates to help you get started
    8. 1.8. Xcode is the hub of your iPhone project...
    9. 1.9. ...and plays a role in every part of writing your app
    10. 1.10. Build your interface using... Interface Builder
    11. 1.11. Add the button to your view
    12. 1.12. The iPhone Simulator lets you test your app on your Mac
    13. 1.13. What happened?
      1. 1.13.1. Unless the UI components are hooked up to the code, nothing is going to happen.
    14. 1.14. Use Interface Builder to connect UI controls to code
    15. 1.15. Interface Builder lists which events a component can trigger
    16. 1.16. Elements dispatch events when things happen to them
    17. 1.17. Connect your events to methods
    18. 1.18. Your iPhone Toolbox
  6. 2. iPhone app patterns: Hello @twitter!
    1. 2.1. First we need to figure out what Mike (really) wants
    2. 2.2. App design rules—the iPhone HIG
      1. 2.2.1. Application types
    3. 2.3. HIG guidelines for pickers and buttons
    4. 2.4. Create a new View-based project for InstaTwit
      1. 2.4.1. Start with the view layout
    5. 2.5. The life of a root view
    6. 2.6. First, get the data from Mike
    7. 2.7. Use pickers when you want controlled input
      1. 2.7.1. When in doubt, check out Apple's API documentation
    8. 2.8. Fill the picker rows with Mike's data
    9. 2.9. Pickers get their data from a datasource...
      1. 2.9.1. ...and tell their delegates when something happens.
    10. 2.10. There's a pattern for that
      1. 2.10.1. Controls have their own specific datasources and delegates
      2. 2.10.2. Protocols tell you what methods (messages) you need to implement
    11. 2.11. First, declare that the controller conforms to both protocols
      1. 2.11.1. Next, add Mike's activities and feelings to the implementation file
    12. 2.12. The datasource protocol has two required methods
    13. 2.13. Connect the datasource just like actions and outlets
    14. 2.14. There's just one method for the delegate protocol
    15. 2.15. The button needs to be connected to an event
      1. 2.15.1. Without an action, your button won't work!
    16. 2.16. Add the IBOutlet and property to our view controller
    17. 2.17. Connect the picker to our outlet
    18. 2.18. Use our picker reference to pull the selected values
    19. 2.19. Your iPhone Toolbox
  7. 3. objective-c for the iPhone: Twitter needs variety
    1. 3.1. Renee is catching on....
    2. 3.2. Make room for custom input
    3. 3.3. Header files describe the interface to your class
    4. 3.4. Auto-generated accessors also handle memory management
      1. 3.4.1. Objective-C can automatically release references, too.
    5. 3.5. To keep your memory straight, you need to remember just two things
    6. 3.6. But when Mike's finished typing...
    7. 3.7. Customize your UITextField
      1. 3.7.1. Next change the label on the return key
    8. 3.8. Components that use the keyboard ask it to appear...
      1. 3.8.1. ...by passing messages to other objects
    9. 3.9. Ask the textField to give up focus
    10. 3.10. Messages in Objective-C use named arguments
    11. 3.11. Use message passing to tell our view controller when the Done button is pressed
    12. 3.12. Something's still not right
      1. 3.12.1. Build the tweet with strings
    13. 3.13. Your Objective-C Toolbox
  8. 4. multiple views: A table with a view
    1. 4.1. So, how do these views fit together?
    2. 4.2. The navigation template pulls multiple views together
    3. 4.3. The navigation template starts with a table view
    4. 4.4. A table is a collection of cells
      1. 4.4.1. Each drink gets its own cell... sorta
    5. 4.5. Just a few more drinks
    6. 4.6. Plists are an easy way to save and load data
    7. 4.7. Arrays (and more) have built-in support for plists
    8. 4.8. Use a detail view to drill down into data
    9. 4.9. A closer look at the detail view
    10. 4.10. Use the navigation controller to switch between views
    11. 4.11. Navigation controllers maintain a stack of views
      1. 4.11.1. We'll use the tap notification in the table view delegate
    12. 4.12. Instantiate a view controller like any other class
    13. 4.13. Dictionaries store information as key-value pairs
    14. 4.14. Debugging—the dark side of iPhone development
      1. 4.14.1. Warnings can help find problems without debugging
    15. 4.15. First stop on your debugging adventure: the console
    16. 4.16. Interact with your application while it's running
      1. 4.16.1. And when it's about to stop running
    17. 4.17. Xcode supports you after your app breaks, too
    18. 4.18. The Xcode debugger shows you the state of your application
    19. 4.19. What the heck is going on?
    20. 4.20. Your iPhone Toolbox
  9. 5. plists and modal views: Refining your app
    1. 5.1. It all started with Sam...
      1. 5.1.1. DrinkMixer
    2. 5.2. Use the debugger to investigate the crash
      1. 5.2.1. We're trying to stuff a dictionary into a string
    3. 5.3. Update your code to handle a plist of dictionaries
    4. 5.4. The detail view needs data
    5. 5.5. Each dictionary has all the information we need
    6. 5.6. We have a usability problem
    7. 5.7. Use a disclosure indicator if your cell leads to more information
    8. 5.8. Sales were going strong...
    9. 5.9. Use navigation controller buttons for editing
    10. 5.10. The button should create a new view
    11. 5.11. We need a view... but not necessarily a new view
    12. 5.12. The view controller defines the behavior for the view
    13. 5.13. A nib file contains the UI components and connections...
      1. 5.13.1. ...and information about the nib's File's Owner
    14. 5.14. You can subclass and extend views like any other class
    15. 5.15. Use Xcode to create a view controller without a nib
    16. 5.16. Modal views focus the user on the task at hand...
      1. 5.16.1. ...like adding or editing items
    17. 5.17. Any view can present a modal view
    18. 5.18. Our view doesn't have a navigation bar
    19. 5.19. Create the save and cancel buttons
    20. 5.20. Write the save and cancel actions
    21. 5.21. Your iPhone Toolbox
  10. 6. saving, editing, and sorting data: Everyone's an editor...
    1. 6.1. Sam is ready to add a Red-Headed School Girl...
    2. 6.2. ...but the keyboard is in the way
    3. 6.3. We need to wrap our content in a scroll view
    4. 6.4. The scroll view is the same size as the screen
    5. 6.5. The keyboard changes the visible area
    6. 6.6. iPhone notifies you about the keyboard
    7. 6.7. Register with the default notification center for events
      1. 6.7.1. Then unregister when you're done
    8. 6.8. Keyboard events tell you the keyboard state and size
    9. 6.9. The table view doesn't know its data has changed
    10. 6.10. You need to ask the table view to reload its data
    11. 6.11. The array is out of order, too
      1. 6.11.1. We can sort our array using NSSortDescriptor
    12. 6.12. Table views have built-in support for editing and deleting
    13. 6.13. Your iPhone Development Toolbox
  11. 7. tab bars and core data: Enterprise apps
    1. 7.1. HF bounty hunting
    2. 7.2. Choose a template to start iBountyHunter
    3. 7.3. Drawing how iBountyHunter works...
    4. 7.4. Build the fugitive list view
    5. 7.5. Next up: the captured view
      1. 7.5.1. A view's contents are actually subviews
    6. 7.6. After a quick meeting with Bob...
    7. 7.7. Core Data lets you focus on your app
      1. 7.7.1. But wait, there's more!
    8. 7.8. Core Data needs to know what to load
      1. 7.8.1. We need to define our types...
    9. 7.9. Core Data describes entities with a Managed Object Model
    10. 7.10. Build your Fugitive entity
      1. 7.10.1. Exactly!
    11. 7.11. Whip up a Fugitive class without writing a line
      1. 7.11.1. Our generated Fugitive class matches our Managed Object Model
      2. 7.11.2. NSManagedObject handles storage and memory for generated properties
      3. 7.11.3. NSManagedObject also implements the properties
    12. 7.12. Use an NSFetchRequest to describe your search
      1. 7.12.1. Ask the Managed Object Context to fetch data using your NSFetchRequest
    13. 7.13. Add the database as a resource
      1. 7.13.1. Back to the Core Data stack
    14. 7.14. The template sets things up for a SQLite DB
      1. 7.14.1. iPhone Apps are read-only
    15. 7.15. The iPhone's application structure defines where you can read and write
      1. 7.15.1. Use the Documents directory to store user data
    16. 7.16. Copy the database to the correct place
    17. 7.17. To be continued...
    18. 7.18. Your Core Data Toolbox
  12. 8. migrating and optimizing with core data: Things are changing
    1. 8.1. Bob needs documentation
    2. 8.2. Everything stems from our object model
    3. 8.3. The data hasn't been updated
      1. 8.3.1. Core Data caught a mismatch between our DB and our model
    4. 8.4. Data migration is a common problem
    5. 8.5. We need to migrate the old data into the new model
      1. 8.5.1. Our two models need different versions
    6. 8.6. Xcode makes it easy to version the data model
    7. 8.7. Core Data can "lightly" migrate data
    8. 8.8. Bob has some design input
      1. 8.8.1. The Managed Object Context saves new or changed items
    9. 8.9. A quick demo with Bob
    10. 8.10. Use predicates for filtering data
      1. 8.10.1. NSFetchRequest concepts are nearly identical to SQL
    11. 8.11. We need to set a predicate on our NSFetchRequest
    12. 8.12. Core Data controller classes provide efficient results handling
      1. 8.12.1. Table views and NSFetchedResultsControllers are made for each other
    13. 8.13. Time for some high-efficiency streamlining
    14. 8.14. Next we need to change the search to use the controller...
    15. 8.15. Refactor viewWillAppear to use the controller
    16. 8.16. We need to refresh the data
      1. 8.16.1. NSFetchedResultsController can check for changes
    17. 8.17. Your Data Toolbox
  13. 9. camera, map kit, and core location: Proof in the real world
    1. 9.1. For Bob, payment requires proof!
      1. 9.1.1. Flip over for the detail view!
    2. 9.2. The way to the camera...
      1. 9.2.1. The iPhone isn't the only device using apps
    3. 9.3. There's a method for checking
    4. 9.4. Prompt the user with action sheets
      1. 9.4.1. We'll use action sheets to let the user pick the image source
    5. 9.5. Bob needs the where, in addition to the when
    6. 9.6. Core Location can find you in a few ways
      1. 9.6.1. Core Location relies on the LocationManager
    7. 9.7. Add a new framework
      1. 9.7.1. Then update the header file
    8. 9.8. Just latitude and longitude won't work for Bob
    9. 9.9. Map Kit is new with iPhone 3.0
    10. 9.10. A little custom setup for the map
    11. 9.11. Annotations require a little more finesse
    12. 9.12. Your extras Toolbox
    13. 9.13. It's been great having you here!
  14. A. leftovers: The top 6 things (we didn't cover)
    1. A.1. #1. Internationalization and Localization
      1. A.1.1. Localizing nibs
    2. A.2. Localizing string resources
    3. A.3. Generating your strings file
    4. A.4. #2. UIWebView
      1. A.4.1. Using UIWebView
    5. A.5. UIWebView properties
      1. A.5.1. Loading generated content
      2. A.5.2. The UIWebView supports a delegate, too
    6. A.6. #3. Device orientation and view rotation
      1. A.6.1. The view controller tells the iPhone OS what orientations it supports
    7. A.7. Handling view rotations
    8. A.8. Handling rotation with two different views
    9. A.9. #4. View animations
      1. A.9.1. Animating table view updates
      2. A.9.2. Animating view and control changes
    10. A.10. #5. Accelerometer
      1. A.10.1. All you need is the UIAccelerometer
    11. A.11. Understanding the device acceleration
    12. A.12. #6. A word or two about gaming...
      1. A.12.1. Multitouch
    13. A.13. Quartz and OpenGL
      1. A.13.1. Quartz
      2. A.13.2. OpenGL
      3. A.13.3. Game Kit
  15. B. preparing an app for distribution: Get ready for the App Store
    1. B.1. Apple has rules
      1. B.1.1. Start at the Apple Developer Portal
      2. B.1.2. First get your Development Certificate
    2. B.2. The Provisioning Profile pulls it all together
    3. B.3. Keep track in the Organizer
    4. B.4. A few final tips...
O'Reilly logo

Chapter 4. multiple views: A table with a view

I like my coffee with two sugars, cream, a sprinkle of cinnamon, stirred twice, then ...

Most iPhone apps have more than one view.

We've written a cool app with one view, but anyone who's used an iPhone knows that most apps aren't like that. Some of the more impressive iPhone apps out there do a great job of moving through complex information by using multiple views. We're going to start with navigation controllers and table views, like the kind you see in your Mail and Contact apps. Only we're going to do it with a twist...

Look, I don't have time for posting to Twitter. I need to know a ton of drink recipes every night. Is there an app for that?

Sam, bartender at the HF Lounge

So, how do these views fit together?

Before you pick the template for our bartending app, take a minute to look at how you want the user to interact with the drink information. We're going to have a scrollable list of drink names, and when the user taps on a row, we'll show the detailed drink information using view #2, our detailed view. Once our user has seen enough, they're going to want to go back to the drink list.

Title

Drink #1

Drink #1

Drink #1

Name:

Ingredients:

Directions:

Once our users are done with the detailed information, the Navigation bar gives them a way to get back to the list.

Name:

Ingredients:

Directions:

We're going to want some kind of transition between these views...

We need a list of items to work with...

Title

Name:

Ingredients:

Directions:

We're going to be coming in and out of this view a lot – each time our user selects a drink.

The navigation template pulls multiple views together

For this app, we're going to use a Navigation-based project. To get started, go into Xcode and choose the File→New Project option. Choose the Navigationbased application and save it as DrinkMixer.proj. Make sure that "Use Core Data for storage" is not checked.

The navigation template comes with a lot of functionality built in:

Just like the name says, a navigation controller is built in. It provides back buttons, title bars, and a view history that will keep your user moving through the data without getting lost.

Don't check Core Data. We'll use that later in the book.

Lemon Drop

Firecracker

We have hierarchical data to organize. The navigation template helps us to move through the data, starting with a table view.

Lemon Drop: Citron vodka, lemon, and sugar. Add sugar to the rim of glass, pour ingredients into shaker...

Firecracker: Wild turkey and hot sauce. Pour ingredients into a rocks glass filled with ice.

The Navigation Controller provides transitions between views with animations.

The navigation template starts with a table view

The navigation template comes with a navigation controller and a root view that the controller displays on startup. That root view is set up as a table view by default, and that works great for our app. A table view is typically used for listing items, one of which then can be selected for more details about that item.

Navigation template

The navigation controller provides a navigation bar.

This is where you'll find the back buttons, forward buttons, and the title of the view you're in.

The table view

The table view provides an easy way to work with data. It starts with an empty, scrollable list for the main view of your application.

there are no: Dumb Questions

Q:

If the navigation template is about handing lots of views, why does it only come with one?

A:

Most navigation-based applications start out with a table view and show detailed views from there. How many detailed views, what they look like, etc. are very application-specific, so you have to decide what views you want and add those views. The navigation template doesn't assume anything beyond the initial table view.

Q:

What built in apps on iPhone use the Navigation control?

A:

Contacts and Mail, which are both core iPhone apps, use this design. It's a good idea to get into those apps on your phone to see how the entire template is implemented. For a neat twist, take a look at the Messages (SMS) app. That one uses a navigation controller but frequently starts in the "detail" view, showing the last person you sent or received a message from.

Q:

Do I have to use a table view for my root view?

A:

No, it's just the most common, since it provides a natural way to show an overview of a lot of data and have the user drill down for more information. Table views are very customizable, too, so some apps that might not seem like table views really are, like Notes or the iTunes store, for example.

A table is a collection of cells

The UITableView provides a lot of the functionality we need right away, but it still needs to know what data we're actually trying to show and what to do when the user interacts with that data. This is where the datasource and delegate come in. A table view is easy to customize and is set up by the template to talk to the datasource and delegate to see what it needs to show, how many rows, what table cells to use, etc.

The navigation controller, not the table view, provides the navigation bar. Since we're in interface builder, this is just a simulated one.

Table views have built-in support for editing their contents, including moving rows around, deleting rows, and adding new ones.

Table views can tell you when your user taps on a cell. It'll tell you the section and row that was tapped.

We're using the default table view cell, but you can create your own and lay them out any way you want.

Table views try to conserve memory by reusing cells when they scroll off the screen.

A table can only have one column, but you can put whatever you want in that column by customizing your table cells.

A table can have multiple sections, and each section can have a header and a footer. We only have one section, so we don't need either for DrinkMixer.

A table view is made up of multiple table cells. The table view will ask how many cells (or rows) are in each section.

there are no: Dumb Questions

Q:

How do cells get into that reusable list to begin with?

A:

The table view handles that. When cells scroll off the screen (either the top or the bottom,) the table view will queue up cells that are no longer needed. When it asks the datasource for a cell for a particular row, you can check that queue of cells to see if there are any available for use.

Q:

I don't understand the cell identifier... does it have to be "Cell"?

A:

No—that's just the default. When you do more complex table views, you can create custom cell types depending on what data you're trying to display. You use the cell identifier to make sure that when you ask for a reusable cell, the table view gives you back the type you expect. The identifier can be anything you want—just make sure you have a unique name for each unique cell type you use.

Drink List: Firecracker Lemon Drop Mojito

Wait, memory on the iPhone is a big deal, right? How can we put in all those drinks?

Like everything else on iPhone, the UITableView has to worry about memory.

So, how does it balance concerns about memory with an unknown amount of data to display? It breaks things up into cells.

Each drink gets its own cell... sorta

The UITableView only has to display enough data to fill an iPhone screen—it doesn't really matter how much data you might have in total. The UITableView does this by reusing cells that scrolled off the screen.

The cells that are off the view go into a bucket until iPhone needs memory or the table view can reuse them when the user scrolls.

When the table view has to scroll a new row onto the screen, it asks the datasource for a cell for that row.

ecracker

Captain

As the user scrolls, some cells slide off the screen.

This is the active view with the table cells that are currently visible.

Firecracker

Lemon Drop

Absolut Mixer

Bee Stinger

Cupid's

Mojito

Miami Vice

Captain

The datasource checks the cell bucket to see if there are any cells available to reuse. If so, it just replaces the row's contents and returns the row.

Datasource

If there aren't any for reuse, the datasource creates a new one and sets its content.

The tableview takes the new cell and scrolls it in...

there are no: Dumb Question

Q:

You mentioned the table view's datasource and delegate, but why didn't I have to declare anything like we did with UIPickerView?

A:

Great catch. Normally you would, but the navigation-based template we used already set this up. To see what's happening, look at the RootViewController.h file. You'll see that it is a subclass of UITableViewController, and that class conforms to the UITableViewDataSourceProtocol and the UITableViewDelegateProtocol. If you look in RootViewController.xib, you'll see that the table view's datasource and delegate are both set to be our RootViewController. If we weren't using a template, you'd have to set these up yourself (we'll revisit this in Chapter 7).

Q:

I noticed we used an NSMutableArray. Is that because we had to initialize it?

A:

No—both NSMutableArray and NSArray can be initialized with values when you create them. We're using an NSMutableArray because we're going to manipulate the contents of this array later. We'll get there in a minute.

Q:

What's the nil at the end of the rink names when we create the drink array?

A:

NSMutableArray's initializer takes a variable number of arguments. It uses nil to know it's reached the end of the arguments. The last element in the array will be the value before the nil—nil won't be added to the array.

Q:

Tell me again about that @ symbol before our drink names?

A:

The @ symbol is shorthand for creating an NSString. NSArrays store arrays of objects, so we need to convert our text names (char*s) to NSStrings. We do that by putting an @ in front of the text constant.

Q:

When we customized the table view cells, we used the cell.textLabel. Are there other labels? What's the difference between cell.textLabel and cell.text?

A:

Before iPhone 3.0, there was just one label and set of disclosure indicators in the default cell, and it was all handled by the cell itself. You just sent the text you wanted on the cell.text property. Nearly everyone wanted a little more information on the table cells, so in iPhone 3.0, Apple added a few different styles with different label layouts. Once they did that, they introduced specific properties for the different text areas, like textLabel, detailLabel, etc., and deprecated the old cell.text property. You shouldn't use cell.text in your apps—Apple will likely remove it at some point in the future. We'll talk more about the other labels later in the chapter.

Q:

You mention that we can use section headers and footers—how do you specify those?

A:

The datasource is responsible for that information, too. There are optional methods you can provide that return the title for section headers and the title for section footers based on the section number. They work a lot like our cellForRowAtIndexPath, except they only return strings.

Q:

What's the difference between a plain table view and a grouped table view?

A:

The only difference is the appearance. In a plain table view, like the one we're using, all the sections touch each other and are separated by the section header and footer if you have them. In a grouped table view, the table view puts space between the sections and shows the section header in bigger letters. Take a look at your contact list, then select a contact. The first view, where all of your contacts are listed together and separated by letters is a plain table view. The detailed view, where the phone numbers are separated from email addresses, etc, is a grouped table view.

Just a few more drinks

The drink menu at Head First Lounge has 40 cocktails.

Firecracker

Lemon Drop

Mojito

Absolut Mixer

Bee Stinger

Cupid's Cocktail

Strawberry Daquiri

Long Island Ice Tea

Captain and Coke

Miami Vice

Boxcar

Cat's Meow

Apple Martini

Manhattan

After Dinner Mint

Red Rudolph

Day at the Beach

Melon Tree

Rum Runner

Blue Dog

Key West Lemonade

Neapolitan

Polo Cocktail

Purple Yummy

Neon Geek

Flaming Nerd

Letter Bomb

Bookmaker's Luck

Baked Apple

Deer Hunter

Mexican Bomb

Aftershock

Black Eyed Susan

Beetle Juice

Terminator

Gingerbread Man

Lost in Space

Music City Sunset

Cafe Joy

Sandbar Sleeper

Get ready to start typing...

This sucks. Can't we just import the list Sam sent us somehow?

We could, but not the way we're set up now.

Since the drinks are populated with an array that's hardcoded into the implementation file, we can't import anything.

What would work well is a standardized way to read and import data; then we would be able to quickly get that drink list loaded.

Plists are an easy way to save and load data

Plist stands for "property list" and it has been around for quite a while with OS X. In fact, there are a number of plists already in use in your application. We've already worked with the most important plist, DrinkMixer-Info.plist. This is created by Xcode when you first create your project, and besides the app icons, it stores things like the main nib file to load when the application starts, the application version, and more. Xcode can create and edit these plists like any other file. Click on DrinkMixer-Info.plist to take a look at what's inside

Some of these items are obvious, like the icon file and the main nib to load.

Others are less obvious, but we'll talk more about them in later chapters.

Built-in types can save and load from plists automatically

All of the built-in types we've been using, like NSArray and NSString, can be loaded or saved from plists automatically. We can take advantage of this and move our drink list out of our source code.

- (void)viewDidLoad {
  [super viewDidLoad];

     NSMutableArray* tmpArray = [[NSMutableArray alloc]
 initWithObjects:@"Firecracker", @"Lemon Drop", @"Mojito",nil];
     self.drinks = tmpArray;
     [tmpArray release];

  // Uncomment the following line to display an Edit
button in the navigation bar for this view controller.
  // self.navigationItem.rightBarButtonItem = self.
editButtonItem;

}

We'll move our drink list out of the source code here and into a plist instead...

Arrays (and more) have built-in support for plists

Changing the array initialization code to use the plist is remarkably easy. Most Cocoa collection types like NSArray ad NSDictionary have built-in support for serializing to and from a plist. As long as you're using built-in types (like other collections, NSStrings, etc.,) you can just ask an array to initialize itself from a plist.

The only piece missing is telling the array which plist to use. To do that, we'll use the project's resource bundle, which acts as a handle to applicationspecific information and files.

-(void)viewDidLoad {
  [super viewDidLoad];
     NSString *path = [[NSBundle mainBundle] pathForResource:
@"DrinkArray" ofType:@"plist"];
     NSMutableArray *tmpArray = [[NSMutableArray alloc]
 initWithContentsOfFile:path];
     self.drinks = tmpArray;
     [tmpArray release];
                            ...

Ask the app bundle for a path to our DrinkArray plist.

Initialize the array using the contents of the plist.

RootViewController.m

Now we just need to get that detail view all set up, right?

Creating your detail view will complete the app.

The entire list of drinks is great, but Sam still needs to know what goes in them and how to make them. That information is going to go in the detail view that we sketched up earlier.

Use a detail view to drill down into data

Earlier, we classified DrinkMixer as a productivity app and we chose a navigation controller because we have hierarchical data. We have a great big list of drinks loaded, but what Sam needs now is the detailed information for each drink: what are the ingredients, how do you mix them, etc. Now we'll use that navigation controller to display a more detailed view of a drink from the list.

The standard pattern for table views is that you show more information about an item when a user taps on a table cell. We'll use that to let the user select a drink then show our detailed view. The detail view follows the same pattern as our other views:

When the user taps on a drink, we'll display the detail view.

Touch here.

View Controller

The table view's controller (our RootViewController) will get the touch information. It will tell the nav controller to show the detailed view.

The detail view shows all the elements that make up a drink - the ingredients and how to mix them.

Detail

View

Since the detail view only cares about the specific drink it's showing details for, the datasource will focus on one drink.

Datasource

View Controller

Just like our other views, the detail view will have a view controller. This one will be responsible for filling in the detail view.

A closer look at the detail view

We sketched out the detail view earlier—but we need to look more closely at what we're about to build.

The back button comes with the nav controller

It will be populated with "Name:" and the drink info, so we don't need a label

A couple of labels for the bottom two fields

Back button

UITextField for the drink name

UITextView for the ingredients

UITextView for the directions

Let's start building...

there are no: Dumb Questions

Q:

We keep drawing the datasource, view, and view controller as separate things, but then we stick them together into the same class. What's going on?

A:

It's all about the pattern. In general, you'll have a few defined in a nib, a view controller backing it, and a set of data it needs to work on. Whether these are combined into one class or not really depends on the complexity of your application. If you're not using Interface Builder, you can go completely off the deep end and have your single class create the view programmatically. We'll show more of that later in the book. Conceptually, however, you still have a view that's calling into the view controller when things happen. Likewise, you usually have one or more datasource protocols being realized somewhere that are providing data to your view.

Q:

Why do we have to move the *.xib file into the Resources group?

A:

You don't have to, but we recommend it to help keep your code organized. Different developers use different groups, things like "User Interface", "Business Objects", "Data Objects", etc. Xcode really doesn't care; it's just important that you know how your code is organized and you can find what you're looking for. Reusing a structure that others will recognize is a good practice so people can pick up your code quickly and you can understand their code. We use the templated defaults in this book.

Q:

What are other ways to save data?

A:

There are quite a few of them. We'll cover the more common ones in this book in different projects. The one you're using now, plists, is the simplest, but it does limit what you can save and load. That doesn't make it bad; if it works for what you need, it's a fine solution—it's just too limited for everything. There's a serialization method called NSCoding that works well for custom objects, but can make version migration a challenge. iPhone supports saving and loading to a database using SQLite. This used to be the preferred way to go if you have a lot of data or need to search and access it without loading it all into memory. However, with iPhone 3.0, Apple introduced Core Data. Core Data is a very powerful framework that provides an OO wrapper on persistence and has nearly all of the benefits of using SQLite. It's definitely not trivial to get started, but it's really powerful. We'll build an app on it later.

Q:

Why didn't you use a label for the name field?

A:

UITextFields allow you to have placeholder text that appears in the field when it's empty. Rather than using up screen space with a Name label, we chose to use the placeholder. If the meaning of the text shown on the screen is obvious to the user, consider using placeholder text.

Q:

So why didn't we use it for the ingredients and directions?

A:

We could have, but since those contain multiple lines of text, we wanted to break them up with labels clearly showing what they were. Ultimately it's an aesthetic and usability decision, not a technical one.

OK, so I have an order for a Melon Tree... but I still don't see the drink details.

Touch here

We still need to get that detail view to load when Sam selects a drink.

Use the navigation controller to switch between views

Now that we've got the table view populated and the detail view built, it's time to manage moving between the two views. The navigationbased template comes preloaded with the functionality we need:

  • A view stack for moving between views

    As users move back and forth, you can ask the navigation controller to display the appropriate view. The navigation controller keeps track of where the users are and gives them buttons to go back.

  • A navigation bar for buttons and a title

    The navigation controller interacts with the navigation bar to display buttons that interact with the view being shown, along with a title to help the users know where they are.

  • A navigation toolbar for view-specific buttons

    The navigation controller can display a toolbar at the bottom of the screen that shows custom buttons for its current view.

The UINavigationController supports a delegate, called the UINavigationControllerDelegate, that gets told when the controller is about to switch views, but for DrinkMixer we won't need this information. Since the views get told when they're shown and hidden, that's all we need for our app.

Now we need to get the table view and nav controller working together to display the detail view.

Navigation controllers maintain a stack of views

We've been dragging the navigation controller along since the beginning of this project, and now we finally get to put it to use. The navigation controller maintains a stack of views and displays the one on top. It will also automatically provide a back button, as well as the cool slide-in and out animations. We're going to talk more about the whole navigation controller stack in the next chapter, but for now, we're just going to push our new view onto the stack and let the controller take care of the rest. We just need to figure out how to get that new view.

When a row is tapped, tableview:did SelectRowAtIndexPath: indexPath is sent to the delegate.

Add Drink View

Drink Table View

Once the new view is created, we'll use the navigation controller to push the view onto the screen.

Delegate

When the delegate method is called, our RootViewController (the delegate) needs to create and push the detail view controller.

We'll use the tap notification in the table view delegate

When a table row is touched, the table view calls tableview:didSelectRowA tIndexPath: on its delegate. The table passes along an NSIndexPath (just like cellForRowAtIndexPath) that tells us which row was selected.

Here's where it gets interesting: our RootViewController is our delegate, so it needs to hand off control to the view controller for our detail view...

Instantiate a view controller like any other class

The only piece left to create is the view controller. Instantiating a view controller is no different than instantiating any other class, with the exception that you can pass in the nib file it should load its view from:

[[DrinkDetailViewController alloc] initWithNibName:@
"DrinkDetailView Controller" bundle:nil];

Once we've created the the detail view controller, we'll ask the NavigationController to push the new view controller onto the view stack. Let's put all of this together by creating the callback into the delegate and creating the new view controller to push onto the stack:

#import "RootViewController.h"
#import "DrinkDetailViewController.h"

Since we're going to create the new view controller, we need to include its header.

// Override to support row selection in the table view.
  - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath {

    // Navigation logic may go here -- for example, create and
 push another view controller.
  DrinkDetailViewController *drinkDetailViewController =
[[DrinkDetailViewController alloc] initWithNibName:@"
DrinkDetailViewController" bundle:nil];
  [self.navigationController pushViewController:
drinkDetailViewController animated:YES];
  [drinkDetailViewController release];
}

Here's the delegate callback - the indexPath tells us which row (drink) was selected.

Instantiate the controller...

...then push it onto the navigation stack.

Now that the navigation controller has the detail controller, we can release our reference to it.

RootViewController.m

Let's try this out...

So, now we can get to the detail view from the drink list, but there aren't any details in there. We don't have that info in our plist, do we?

We've outgrown our array

All that's left is to get the ingredients and directions in the detail view, and we'll have a bartender's brain. To save you from having to type in the ingredients and directions, we put together a new file with all of the extra information. The problem is we can't just jam that information into an array. To add the drink details to this version, we need a different data model.

Dictionaries store information as key-value pairs

Our current drink plist is just a single array of drink names. That worked great for populating the table view with just drink names, but doesn't help us at all with drink details. For this plist, instead of an array of strings, we created an array of dictionaries. Within each dictionary are three keys: name, ingredients, and directions. Each of these have string values with the corresponding information. Since NSDictionary adopts the NSCoding protocol, it can be saved and loaded in plists just like our basic array from before.

there are no: Dumb Questions

Q:

You keep talking about NSCoding. What is that?

A:

NSCoding is a protocol that works with the encoding and decoding of objects. Working with this protocol means dealing with how an object can be stored on disk or distributed throughout the device. For more information about NSCoding, see the Apple documentation.

Q:

Where did the back button in the detail view come from? We didn't do that...

A:

It's automatic functionality that comes with the navigation controller. When you added a title for the main view, the navigation controller kept track of that name as part of the view stack for navigation, and added a back button with the title in it. So yeah, you did do that!

Debugging—the dark side of iPhone development

Something has gone wrong, but honestly, this is a pretty normal part of the development process. There are lots of things that could cause our application to crash, so we need to figure out what the problem is.

Warnings can help find problems without debugging

In general, if your application doesn't build, Xcode won't launch it—but that's not true for warnings. Xcode will happily compile and run an application with warnings and your only indication will be a little yellow yield sign in the bottom right corner of Xcode. Two minutes spent investigating a warning can save hours of debugging time later.

3 errors and 3 warnings... the errors have to be fixed. The warnings should be investigated—and probably fixed, too.

That's not our problem, though: our code should be warning and compile-error-free. The good news is that when an app crashes in the Simulator, it doesn't go away completely (like it would on a real device). Xcode stops the app right before the OS would normally shut it down. Let's use that to see what's going on.

Time for some debugging...

First stop on your debugging adventure: the console

We need to figure out why our app crashed, and thankfully, Xcode has a lot of strong debugging capabilities. For now we're just going to look at the information it gave us about the crash, but later in the book we'll talk about some of the more advanced debugging features.

Since you ran the program in the simulator, the console should be up. Here's what ours looks like:

The toolbar contains typical debugging commands, like stopping your application, restarting it, and continuing after hitting a breakpoint.

The console has the information about what happened that caused our application to be shut down. It doesn't tell us why it happened, though...

The console tells us that our app was shut down because of an uncaught exception, and what that exception was.

The console also gives us a stack trace of where our application was, but there's a much better view of that coming up in a second...

Interact with your application while it's running

The console is a very powerful debugging tool. Some of the best debugging techniques involve well-placed logging messages using NSLog(...). This information is printed into the console and can help you diagnose problems quickly. The console isn't just read-only, though; it is your window into your running application. We'll see log messages displayed in the console, and when your application hits a breakpoint, you'll be placed at the console prompt. From there you can use debugging commands like print, continue, where, up, and down to inspect the state of your application.

The console debugger is actually the open source gdb prompt, so nearly all gdb commands work here.

And when it's about to stop running

In this case, we're dealing with a nearly dead application, but the idea is the same. Since DrinkMixer has crashed, Xcode provides you with the basic information of what went wrong. In our case, an "unrecognized selector" was sent to an object. Remember that a selector is basically a method call—it means that some code is trying to invoke methods on an object and those methods don't exist.

The console prompt lets you interact with your application at the command line.

But Xcode doesn't stop at the command line. It has a full GUI debugger built right in. Let's take a look...

Xcode supports you after your app breaks, too

So far we've used Xcode to write code and compile and launch our applications. Its usefulness doesn't stop once we hit the "Build and Debug" button. First, we can set breakpoints in our code to let us keep an eye on what's going on. Simply click in the gutter next to the line where you want to set a breakpoint. Xcode will put a small blue arrow next to the line and when your application gets to that line of code, it will stop and let you poke around using the console.

This switch indicates whether the breakpoints are on or not.

When the breakpoints are on, you'll get this cool can of bug spray icon...

Once your app hits a breakpoint, Xcode will insert Step Into, Step Over, Continue, and Debugger buttons to let you walk through your code.

To set a breakpoint, just click here.

Click on the small bug spray icon or press Shift-

Xcode supports you after your app breaks, too

The Xcode debugger shows you the state of your application

The debugger shows your code and also adds a stack view and a window to inspect variables and memory. When you click on a stack frame, Xcode will show you the line of code associated with that frame and set up the corresponding local variables. There isn't anything in the debugger window you couldn't do with the console, but this provides a nice GUI on top of it.

Here are the Step and Continue buttons to let you walk through your code.

Here's the stack from your app at the current breakpoint (or crash...). If you click on a frame, Xcode will show you the corresponding code.

Xcode shows you your app's variables (local, global, etc.) in this view.

Here's our uncaught exception for the unrecognized selector again...

What the heck is going on?

Our application is crashing, and it's not at the array loading code. Open up the debugger and click on the topmost frame that contains our code. It will show you the line that's causing the problem... see what's wrong?

To be continued...

MultipleViewscross

Take what you've learned about the navigation controller and multiple views to fill in the blanks.

Across

3. The set of views that the nav controller deals with.

6. Dictionaries use __________ to organize data.

8. The screen that gives you output from the app.

9. A template that combines a table view and nav controls.

10. Has cells that need to be customized to work.

Down

1. A more versatile way to manage data beyond an array.

2. DrinkMixer is this type of app.

4. To use a new class you need to ___________ it.

5. The @ symbol is shorthand for creating one of these.

7. A tool in Xcode to help fix broken code.

Your iPhone Toolbox

The best content for your career. Discover unlimited learning on demand for around $1/day.