iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) (37 page)

Read iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) Online

Authors: Aaron Hillegass,Joe Conway

Tags: #COM051370, #Big Nerd Ranch Guides, #iPhone / iPad Programming

BOOK: iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides)
13.61Mb size Format: txt, pdf, ePub
Implementing data source methods

In
ItemsViewController.m
, import
BNRItemStore.h
and
BNRItem.h
and update the designated initializer to add 5 random items to the
BNRItemStore
.

 
#import "ItemsViewController.h"
#import "BNRItemStore.h"
#import "BNRItem.h"
@implementation ItemsViewController
- (id)init
{
    // Call the superclass's designated initializer
    self = [super initWithStyle:UITableViewStyleGrouped];
    if (self) {
        for (int i = 0; i < 5; i++) {
            [[BNRItemStore sharedStore] createItem];
        }
    }
    return self;
}
 

Now that there are some items in the store, you need to teach
ItemsViewController
how to turn those items into rows that its
UITableView
can display. When a
UITableView
wants to know what to display, it sends messages from the set of messages declared in the
UITableViewDataSource
protocol.

 

From the
Help
menu, choose
Documentation and API Reference
to open the iOS SDK documentation. Find the
UITableViewDataSource
protocol documentation (
Figure 9.10
).

 

Figure 9.10  UITableViewDataSource protocol documentation

 

There are many methods here, but notice the two marked
required method
. For
ItemsViewController
to conform to
UITableViewDataSource
, it must implement
tableView:numberOfRowsInSection:
and
tableView:cellForRowAtIndexPath:
. These methods tell the table view how many rows it should display and what content to display in each row.

 

Whenever a
UITableView
needs to display itself, it sends a series of messages (the required methods plus any optional ones that have been implemented) to its
dataSource
. The required method
tableView:numberOfRowsInSection:
returns an integer value for the number of rows that the
UITableView
should display. In the table view for
Homepwner
, there should be a row for each entry in the store (
Figure 9.11
).

 

Figure 9.11  Obtaining the number of rows

 

In
ItemsViewController.m
, implement
tableView:numberOfRowsInSection:
. This method returns the number of rows to display as an
NSInteger
, which is just another name for
int
.

 
- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
    return [[[BNRItemStore sharedStore] allItems] count];
}

Wondering about the section that this method refers to? Table views can be broken up into sections, and each section has its own set of rows. For example, in the address book, all names beginning with

D

are grouped together in a section. By default, a table view has one section, and in this chapter, we will work with only one. Once you understand how a table view works, it is not hard to use multiple sections. In fact, using sections is the first challenge at the end of this chapter.

 

The second required method in the
UITableViewDataSource
protocol is
tableView:cellForRowAtIndexPath:
. To implement this method, we’ll need to learn about another class –
UITableViewCell
.

 
UITableViewCells

A
UITableViewCell
is a subclass of
UIView
, and each row in a
UITableView
is a
UITableViewCell
. (Recall that a table in iOS can only have one column, so a row only has one cell.) The
UITableViewCell
s are subviews of the
UITableView
.

 

A cell itself has one subview – its
contentView
(
Figure 9.12
). The
contentView
is the superview for the content of the cell. It also can draw an accessory indicator. The accessory indicator shows an action-oriented icon, such as a checkmark, a disclosure icon, or a fancy blue dot with a chevron inside. These icons are accessed through pre-defined constants for the appearance of the accessory indicator. The default is
UITableViewCellAccessoryNone
, and that’s what we’ll use in this chapter. But you’ll see the accessory indicator again in
Chapter 15
. (Curious now? See the reference page for
UITableViewCell
for more details.)

 

Figure 9.12  UITableViewCell layout

 

The real meat of a
UITableViewCell
is the three subviews of the
contentView
. Two of those subviews are
UILabel
instances that are properties of
UITableViewCell
named
textLabel
and
detailTextLabel
. The third subview is a
UIImageView
called
imageView
(
Figure 9.13
). In this chapter, we’ll only use
textLabel
.

 

Figure 9.13  UITableViewCell hierarchy

 

Each cell also has a
UITableViewCellStyle
that determines which subviews are used and their position within the
contentView
. Examples of these styles and their constants are shown in
Figure 9.14
.

 

Figure 9.14  UITableViewCellStyles

 
Creating and retrieving UITableViewCells

In this chapter, each cell will display the
description
of a
BNRItem
as its
textLabel
. To make this happen, you need to implement the second required method from the
UITableViewDataSource
protocol,
tableView:cellForRowAtIndexPath:
. This method will create a cell, set its
textLabel
to the
description
of the corresponding
BNRItem
, and return it to the
UITableView
(
Figure 9.15
).

 

Figure 9.15  UITableViewCell retrieval

 

How do you decide which cell a
BNRItem
corresponds to? One of the parameters sent to
tableView:cellForRowAtIndexPath:
is an
NSIndexPath
, which has two properties:
section
and
row
. When this message is sent to a data source, the table view is asking,

Can I have a cell to display in section X, row Y?

Because there is only one section in this exercise, the table view only needs to know the row. In
ItemsViewController.m
, implement
tableView:cellForRowAtIndexPath:
so that the
n
th row displays the
n
th entry in the
allItems
array.

 
- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Create an instance of UITableViewCell, with default appearance
    UITableViewCell *cell =
        [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                reuseIdentifier:@"UITableViewCell"];
    // Set the text on the cell with the description of the item
    // that is at the nth index of items, where n = row this cell
    // will appear in on the tableview
    BNRItem *p = [[[BNRItemStore sharedStore] allItems]
                                    objectAtIndex:[indexPath row]];
    [[cell textLabel] setText:[p description]];
    return cell;
}
 

Build and run the application now, and you’ll see a
UITableView
populated with a list of random
BNRItem
s. Yep, it was that easy. You didn’t have to change anything about
BNRItem
– you simply changed the controller object and let the controller interface with a different view. This is why Model-View-Controller is such a powerful concept. With a minimal amount of code, you were able to show the same data in an entirely different way.

 
Reusing UITableViewCells

iOS devices have a limited amount of memory. If we were displaying a list with thousands of entries in a
UITableView
, we would have thousands of instances of
UITableViewCell
. And your long-suffering iPhone would sputter and die. In its dying breath, it would say

You only needed enough cells to fill the screen... arrrghhh!

It would be right.

 

To preserve the lives of iOS devices everywhere, you can reuse table view cells. When the user scrolls the table, some cells move offscreen. Offscreen cells are put into a pool of cells available for reuse. Then, instead of creating a brand new cell for every request, the data source first checks the pool. If there is an unused cell, the data source configures it with new data and returns it to the table view.

 

Figure 9.16  Reusable UITableViewCells

 

There is one problem: sometimes a
UITableView
has different types of cells. Occasionally, you have to subclass
UITableViewCell
to create a special look or behavior. However, different subclasses floating around the pool of reusable cells create the possibility of getting back a cell of the wrong type. You must be sure of the type of the cell returned to you so that you can be sure of what properties and methods it has.

 

Note that you don’t care about getting any specific cell out of the pool because you’re going to change the cell content anyway. What you need is a cell of a specific type. The good news is every cell has a
reuseIdentifier
property of type
NSString
. When a data source asks the table view for a reusable cell, it passes a string and says,

I need a cell with this reuse identifier.

By convention, the reuse identifier is simply the name of the cell class.

 

In
ItemsViewController.m
, update
tableView:cellForRowAtIndexPath:
to reuse cells:

 
- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    
UITableViewCell *cell =
        
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                              
reuseIdentifier:@"UITableViewCell"];
    // Check for a reusable cell first, use that if it exists
    UITableViewCell *cell =
        [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    // If there is no reusable cell of this type, create a new one
    if (!cell) {
        cell = [[UITableViewCell alloc]
                    initWithStyle:UITableViewCellStyleDefault
                  reuseIdentifier:@"UITableViewCell"];
    }
    BNRItem *p = [[[BNRItemStore sharedStore] allItems]
                                    objectAtIndex:[indexPath row]];
    [[cell textLabel] setText:[p description]];
    return cell;
}

(If you have a table view that uses multiple styles of the same type of cell, you can suffix the reuse identifier with the name of that style, e.g.
UITableViewCell-Default
.)

 

Reusing cells means that you only have to create a handful of cells, which puts fewer demands on memory. Your application’s users (and their devices) will thank you. Build and run the application. The behavior of the application should remain the same.

 

Other books

Bushfire! by Bindi Irwin
Her Outlaw by Geralyn Dawson
Scale-Bright by Benjanun Sriduangkaew
Foolproof by Jennifer Blackwood
The Farwalker's Quest by Joni Sensel
A Fatal Stain by Elise Hyatt
Savage by Robyn Wideman