Bookmark and Share

iPhone SDK Tutorial
Chapter 7. Table Views




7.6 Table View Cells

Though we can do a lot with table views, quite often, we may want to format the data for each row in ways that UItableViewCell does not support. In those cases, we have two options:

  • Adding subviews to the UITableViewCell
  • Creating a subclass of UITableViewCell

In this section, we are going to create a new application with another table view. In each row, we have two lines of information along with two labels. This application will display the country and group names of 32 countries for World Cup 2010, South Africa.

7.6.1 Table View Cells - Adding SubViews

We will see that adding subviews to the table view cell can give us multiline rows.

Let's create a new project using view-based application template with a name "Cells".

Then, add a Table View to the main view. Set table view's delegate and datasource to File's Owner.
Save the nib and back to Xcode.

ViewCell Connection




7.6.2 Table View Cells -ViewController

Let's look at the "CellsViewController.h"

#import <UIKit/UIKit.h>
#define kNameValueTag     1
#define kGroupValueTag    2

@interface CellsViewController : UIViewController 

{
    NSArray    *computers;
}
@property (nonatomic, retain) NSArray *computers;
@end

We are going to add four subviews to the table view cell, and two of those need to be changed for every row. In order to do that, we need a mechanism that allows us to retrieve the two fields from the cell when we update that cell with a particular data of the row.

In CellsViewController.m file, we should set up some data to use, and then implement the table datasource method to feed that data to the table.

#import "CellsViewController.h"

@implementation CellsViewController
@synthesize computers;
- (void)viewDidLoad {
    
    NSDictionary *row1 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"South Africa", @"Name", @"A", @"Group", nil];
    NSDictionary *row2 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Mexico", @"Name", @"A", @"Group", nil];
    NSDictionary *row3 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Uruguay", @"Name", @"A", @"Group", nil];
    NSDictionary *row4 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"France", @"Name", @"A", @"Group", nil];
    NSDictionary *row5 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Argentina", @"Name", @"B", @"Group", nil];
    NSDictionary *row6 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"South Korea", @"Name", @"B", @"Group", nil];
    NSDictionary *row7 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Nigeria", @"Name", @"B", @"Group", nil];
    NSDictionary *row8 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Greece", @"Name", @"B", @"Group", nil];
    NSDictionary *row9 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"England", @"Name", @"C", @"Group", nil];
    NSDictionary *row10 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"United States", @"Name", @"C", @"Group", nil];
    NSDictionary *row11 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Algeria", @"Name", @"C", @"Group", nil];  
    NSDictionary *row12 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Slovenia", @"Name", @"C", @"Group", nil];
    NSDictionary *row13 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Germany", @"Name", @"D", @"Group", nil]; 
    NSDictionary *row14 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Australia", @"Name", @"D", @"Group", nil];		
	
    NSArray *array = [[NSArray alloc] initWithObjects:
                      row1, row2, row3, row4, row5, row6, row7, 
                      row8, row9, row10, row11, row12, row13, row14, nil];
                      
    self.computers = array;
    
    [row1 release];
    [row2 release];
    [row3 release];
    [row4 release];
    [row5 release];
    [row6 release];
    [row7 release];
    [row8 release];
    [row9 release];
    [row10 release];
    [row11 release];
    [row12 release];
    [row13 release];
    [row14 release];
    [array release];
}

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
    self.computers = nil;
}

- (void)dealloc {
    [computers release];
    [super dealloc];
}

#pragma mark -
#pragma mark Table Data Source Methods

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section
{
    return [self.computers count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView 
        cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CellTableIdentifier = @"CellTableIdentifier ";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                             CellTableIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] 
				initWithStyle:UITableViewCellStyleDefault
				reuseIdentifier: CellTableIdentifier] autorelease];
        
        CGRect nameLabelRect = CGRectMake(0, 5, 70, 20);
        UILabel *nameLabel = [[UILabel alloc] initWithFrame:nameLabelRect];
        nameLabel.textAlignment = UITextAlignmentRight;
        nameLabel.text = @"Name:";
        nameLabel.font = [UIFont boldSystemFontOfSize:12];
        [cell.contentView addSubview: nameLabel];
        [nameLabel release];
        
        CGRect groupLabelRect = CGRectMake(0,30, 70, 20);
        UILabel *groupLabel = [[UILabel alloc] initWithFrame:
                               groupLabelRect];
        groupLabel.textAlignment = UITextAlignmentRight;
        groupLabel.text = @"Group:";
        groupLabel.font = [UIFont boldSystemFontOfSize:12];
        [cell.contentView addSubview: groupLabel];
        [groupLabel release];
        
        CGRect nameValueRect = CGRectMake(80, 5, 200, 20);
        UILabel *nameValue = [[UILabel alloc] initWithFrame: 
                              nameValueRect];
        nameValue.tag = kNameValueTag;
        [cell.contentView addSubview:nameValue];
        [nameValue release];
        
        CGRect groupValueRect = CGRectMake(80, 30, 200, 20);
        UILabel *groupValue = [[UILabel alloc] initWithFrame: 
                               groupValueRect];
        groupValue.tag = kGroupValueTag;
        [cell.contentView addSubview:groupValue];
        [groupValue release];
    }
    
    NSUInteger row = [indexPath row];
    NSDictionary *rowData = [self.computers objectAtIndex:row];
    UILabel *name = (UILabel *)[cell.contentView viewWithTag:
                                kNameValueTag];
    name.text = [rowData objectForKey:@"Name"];
    
    UILabel *group = (UILabel *)[cell.contentView viewWithTag:
                                 kGroupValueTag];
    group.text = [rowData objectForKey:@"Group"];
    return cell;
}
@end

The method, viewDidLoad, creates dictionaries. Each dictionary contains the name and group information for one row in the table. The key for the row of name is Name, and the key for the row of group is Group. All the dictionaries is a single array in our application.

Let's look at tableView:cellForRowWithIndexPath: method.
This is where we are getting into some new stuff. The first two lines create an identifier and ask the table to dequeue a table view cell if it has one.

We should create a new cell if the table does not have any cells available. We also need to create and add the subviews that we will be using to implement out two-line-per-row table.
Let's look at the code. First, we create a cell and specify the default style. The style won't matter since we will add our own subviews to display our data instead of using the ones provided.

cell = [[[UITableViewCell alloc] 
        initWithStyle:UITableViewCellStyleDefault
        reuseIdentifier: CellTableIdentifier] autorelease];

Then, we create four UILabels and add them to the table view cell. The table view cell already has a UIView called contentView, which it uses to group all of its subviews. So, we don't add the labels as subviews directly to the table view cell but rather to its content view.

[cell.contentView addSubview: nameLabel];
Tow of these labels contain static text. The label nameLabel contains the text Name: and the label groupLabel contains the text Group:. Those are just static labels that we won't change. The other two labels, however, will be used to display our row0specific data. We assign values to both of them to retrieve these fields later on. For instance, we assign the constant kNameValueTag into nameValue's tag field:

nameValue.tag = kNameValueTag;

Once, we've created our new cell, we use the indexPath argument that was passed in to determine which row the table is requesting a cell for and then use that row value to grab the correct dictionary for the requested row.

NSUInteger row = [indexPath row];
NSDictionary *rowData = [self.computers objectAtIndex:row];

Here, we use the tags we set before to retrieve the label whose value we need to set.

UILabel *name = (UILabel *)[cell.contentView viewWithTag:
                                kNameValueTag];

Once we have that label, we just set its text to one of the values we pull from the dictionary that represents this row.

name.text = [rowData objectForKey:@"Name"];

Build and Run.

CellSubView Result

Being able to add views to the table view gives us more flexibility than using the standard view cell alone. But positioning and adding all the subviews programmatically are may not be the best way of making the table view cell.




7.7 Table View Cells - Subclassing UITableViewCell

We are going to recreate the same two-lined table using Interface Builder by creating a subclass of UITableViewCell and a new nib file. The new nib file will contain the table view cell. After that, when we need a table view cell to represent a row, we will just load in our subclass from the nib file instead of adding subviews to a standard view cell. And we all also use two outlets we will add to set the name and group.

Let's create a new subclass and name it CustonCell.m.
From the Cocoa Touch Class and select Objective-C class.

CocoaTouch ObjectiveC Class

Then, select UITableViewCell subclass and name it CustomCell.m when the new window pops up.
From the Resources folder in Xcode, select Add->New->File... From the new file assitant, choose User Interface and select Empty XIB. Name it CustomCell.xib.

EmptyXIB


7.7.1 Creating UITableViewCell Subclass

We will use outlets in our subclass to set the value that should be changed for each row. By creating a subclass we can make our code much more clean because we will be able to set the labels on each row's cell just by setting properties. For instance:

cell.nameLable = @"EasyToLabel";

New that we have all the pieces we need to move on, let's modify "CustonCell.h".

#import <UIKit/UIKit.h>

@interface CustomCell : UITableViewCell {
    UILabel *nameLabel;
    UILabel *groupLabel;
}
@property (nonatomic, retain) IBOutlet UILabel *nameLabel;
@property (nonatomic, retain) IBOutlet UILabel *groupLabel;
@end

Then, move on to implementation file, "CustomCell.m".

#import "CustomCell.h"

@implementation CustomCell
@synthesize nameLabel;
@synthesize groupLabel;

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
	
    [super setSelected:selected animated:animated];
	
    // Configure the view for the selected state
}
- (void)dealloc {
    [nameLabel release];
    [groupLabel release];
    [super dealloc];
}
@end

7.7.2 Designing Table View Cell

We are about to design the table view cell in Interface Builder. Make sure the two files, "CustomCell.m" and "CustomCell.h", saved.
Let's open Interface Builder by double-clicking "CustomCell.xib" file. We have only two icons in this nib's main window: File's Owner and First Responder.

CustomCellXIB

Drag Table View Cell to the nib's main window.

ViewCell To XIB

Bring up the identity inspector and change the class to CustomCell from UITableViewCell and bring up size inspector to change cell height to 65 from 44 to have more room to play with.

CustomCell Identity CustomCell Size

Even though UITableViewCell is a subclass of UIView, it uses a content view to hold and group its subviews. Open up Custom Cell Content View window by double-clicking Custom Cell icon. Then, you will see a gray dashed rounded rectangle labeled Content View. This indicates that you should add a View from the library. So, drag a View into the Custom Cell window. But when you release the view, it may be the wrong size for our window. So, with the new view selected, bring up size inspector. Change View's size and position to fit the Custom Cell. Set x to 0, y to 0, w to 320, and h to 65.

Drag View To CustomCell Window

While Custom Cell icon selected, bring up attribute inspector. Set the field labeled Identifier to CustomCellIdentifier.

CustomCell Identifier

Now we have a canvas to design our table view cell in Interface Builder. Drag four labels over from the library to the Custom Cell window. Rename them as in the picture below.

NameGroup Labels

Control-drag from the Custom Cell icon to the label on the view, assign it to the outlet nameLabel.

nameLabel Outlet

Then to the other do the same and assign it to the groupLabel outlet.

groupLabel Outlet


7.7.3 Using the New Table View Cell

We need to make some changes to the tableView:cellForRowAtIndexPath: method to use the cell we designed. Here is the new version of the method.

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CustomCellIdentifier = @"CustomCellIdentifier ";
    
    CustomCell *cell = (CustomCell *)[tableView 
                                      dequeueReusableCellWithIdentifier: CustomCellIdentifier];
    if (cell == nil)  
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" 
                                                     owner:self options:nil];
        for (id oneObject in nib)
            if ([oneObject isKindOfClass:[CustomCell class]])
                cell = (CustomCell *)oneObject;
    }
    NSUInteger row = [indexPath row];
    NSDictionary *rowData = [self.computers objectAtIndex:row];
    cell.groupLabel.text = [rowData objectForKey:@"Group"];
    cell.nameLabel.text = [rowData objectForKey:@"Name"];
    return cell;
}

We need to add our new header to "CellsViewController.m"

#import "CustomCell.h"

Since we made the table view cell in a nib file, if there are no reusable cells, we load the one from the nib. When loading the nib, we get an array that contains all the objects in the nib. We will loop through all the objects in the nib and look for an instance of the CustomCell class.

Because we change the height of our table view cell from the default value, there is one other addition we should make. Otherwise, cell may not displayed properly. So, let's add the following delegate method to "CellViewController.m".

- (CGFloat)tableView:(UITableView *)tableView 
       heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return kTableViewRowHeight;
}

But we can't get this value from the cell because this delegate method may be called before the cell exists. So, we should make it hard coded as constant, and we no longer need the tag constants. Here is the new "CellsViewController.h"

#import <UIKit/UIKit.h>
#define kTableViewRowHeight    66
#define kNameValueTag     1 
#define kGroupValueTag    2 

@interface CellsViewController : UIViewController 

{
    NSArray    *computers;
}
@property (nonatomic, retain) NSArray *computers;
@end

Here is the final version of "CellsViewController.m".

#import "CellsViewController.h"
#import "CustomCell.h"

@implementation CellsViewController
@synthesize computers;
- (void)viewDidLoad {
    
    NSDictionary *row1 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"South Africa", @"Name", @"A", @"Group", nil];
    NSDictionary *row2 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Mexico", @"Name", @"A", @"Group", nil];
    NSDictionary *row3 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Uruguay", @"Name", @"A", @"Group", nil];
    NSDictionary *row4 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"France", @"Name", @"A", @"Group", nil];
    NSDictionary *row5 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Argentina", @"Name", @"B", @"Group", nil];
    NSDictionary *row6 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"South Korea", @"Name", @"B", @"Group", nil];
    NSDictionary *row7 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Nigeria", @"Name", @"B", @"Group", nil];
    NSDictionary *row8 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Greece", @"Name", @"B", @"Group", nil];
    NSDictionary *row9 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"England", @"Name", @"C", @"Group", nil];
    NSDictionary *row10 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"United States", @"Name", @"C", @"Group", nil];
    NSDictionary *row11 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Algeria", @"Name", @"C", @"Group", nil];  
    NSDictionary *row12 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Slovenia", @"Name", @"C", @"Group", nil];
    NSDictionary *row13 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Germany", @"Name", @"D", @"Group", nil]; 
    NSDictionary *row14 = [[NSDictionary alloc] initWithObjectsAndKeys:
                          @"Australia", @"Name", @"D", @"Group", nil];		
    NSArray *array = [[NSArray alloc] initWithObjects:
					  row1, row2, row3, row4, row5, row6, row7, 
					  row8, row9, row10, row11, row12, row13, row14, nil];
    self.computers = array;
    
    [row1 release];
    [row2 release];
    [row3 release];
    [row4 release];
    [row5 release];
    [row6 release];
    [row7 release];
    [row8 release];
    [row9 release];
    [row10 release];
    [row11 release];
    [row12 release];
    [row13 release];
    [row14 release];
    [array release];   
}

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
    self.computers = nil;
}

- (void)dealloc {
    [computers release];
    [super dealloc];
}

#pragma mark -
#pragma mark Table Data Source Methods

- (CGFloat)tableView:(UITableView *)tableView 
            heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return kTableViewRowHeight;
}

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section
{
    return [self.computers count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CustomCellIdentifier = @"CustomCellIdentifier ";
    
    CustomCell *cell = (CustomCell *)[tableView 
                                      dequeueReusableCellWithIdentifier: CustomCellIdentifier];
    if (cell == nil)  
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" 
                                                     owner:self options:nil];
        for (id oneObject in nib)
            if ([oneObject isKindOfClass:[CustomCell class]])
                cell = (CustomCell *)oneObject;
    }
    NSUInteger row = [indexPath row];
    NSDictionary *rowData = [self.computers objectAtIndex:row];
    cell.groupLabel.text = [rowData objectForKey:@"Group"];
    cell.nameLabel.text = [rowData objectForKey:@"Name"];
    return cell;
}

@end

Build and Run.

SubClass Result



7.8 Table View Cells - Grouped and Indexed Sections

This section will show another fundamental aspect of tables. We will still use a single table view with no hierarchies yet, however, we will divide data into sections.

Create a new Xcode using view-based application template. Name it Sections.

7.8.1 Building the View

Open the Interface Builder and drop a table view onto the View window. Then, connect the dataSource and delegate connections to the File's Owner icon.

Sections Outlet Connection

While the table view selected, change the table view's Style from Plain to Grouped on the attributes inspector.

Grouped Sections Attributes

Then, we will have a new view. Pictures below show the difference between plain and grouped styles.

Plain Grouped Style Grouped

Save the nib and back to Xcode.



7.8.2 Importing Data

I made a list with countries that will compete in 2010 World Cup with additional group information. The file is sortednames.plist and added to the project's Resources folder.

Sections Data Import plist file

Once it's added to the project, single-click sortednames.plist to get the sense of what it looks like. It's a property list that contains a dictionary, with one entry for each group from A to H. Underneath each group is a list of names of the countries for that group. We are going to use the data from this property list to feed the table view, creating a section for each letter.


7.8.3 Implementing the Controller

Let's look at the interface file, "SectionsViewController.h".

#import <UIKit/UIKit.h>

@interface SectionsViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate>
{
    NSDictionary *names;
    NSArray     *keys;
}
@property (nonatomic, retain) NSDictionary *names;
@property (nonatomic, retain) NSArray *keys;
@end

Then, the implementation file, "SectionsViewController.m".

#import "SectionsViewController.h"

@implementation SectionsViewController
@synthesize names;
@synthesize keys;

- (void)viewDidLoad {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"sortednames"
                                                     ofType:@"plist"];
    NSDictionary *dict = [[NSDictionary alloc] 
                          initWithContentsOfFile:path];
    self.names = dict;
    [dict release];
    
    NSArray *array = [[names allKeys] sortedArrayUsingSelector:
                      @selector(compare:)];
    self.keys = array;
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
    self.names = nil;
    self.keys = nil;
}

- (void)dealloc {
    [names release];
    [keys release];
    [super dealloc];
}

#pragma mark -
#pragma mark Table View Data Source Methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [keys count];
}

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section
{
    NSString *key = [keys objectAtIndex:section];
    NSArray *nameSection = [names objectForKey:key];
    return [nameSection count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSUInteger section = [indexPath section];
    NSUInteger row = [indexPath row];
    
    NSString *key = [keys objectAtIndex:section];
    NSArray *nameSection = [names objectForKey:key];
    
    static NSString *SectionsTableIdentifier = @"SectionsTableIdentifier";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                             SectionsTableIdentifier ];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                       reuseIdentifier: SectionsTableIdentifier ] autorelease];
    }
    
    cell.textLabel.text = [nameSection objectAtIndex:row];
    return cell;
}

- (NSString *)tableView:(UITableView *)tableView 
titleForHeaderInSection:(NSInteger)section
{
    NSString *key = [keys objectAtIndex:section];
    return key;
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    return keys;
}
@end

In the viewDidLoad method, we created an NSDictionary instance from property list. We assign it to names.

self.names = dict;

Then we grabbed all the keys from that dictionary and sorted to make an NSArray.

NSArray *array = [[names allKeys] sortedArrayUsingSelector:
                      @selector(compare:)];

Let's look at the datasource method. The method numberOfSectionsInTableView: specifies the number of sections. It tells the table view that we have one section for each key in our dictionary.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [keys count];
}

Another datasource method, tableView: numberOfRowsInSection:, calculates the number of rows in a specific section. By retrieving the array that corresponds to the section in question, we can get the number of rows for each group. Actually, all sections have 4 rows in our plist, which makes this is not necessary. But we leave it that way just in case.

- (NSInteger)tableView:(UITableView *)tableView 
 numberOfRowsInSection:(NSInteger)section
{
    NSString *key = [keys objectAtIndex:section];
    NSArray *nameSection = [names objectForKey:key];
    return [nameSection count];
}

In tableView: cellForRowAtIndexPath: method, we have to extract both the section and row from the index path and use that to determine which value to use. The section will let us know which array to pull out of the country names dictionary, and then we can use the row to figure out which value from that array to use.

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSUInteger section = [indexPath section];
    NSUInteger row = [indexPath row];
    
    NSString *key = [keys objectAtIndex:section];
    NSArray *nameSection = [names objectForKey:key];
    
    static NSString *SectionsTableIdentifier = @"SectionsTableIdentifier";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                             SectionsTableIdentifier ];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                       reuseIdentifier: SectionsTableIdentifier ] autorelease];
    }
    
    cell.textLabel.text = [nameSection objectAtIndex:row];
    return cell;
}

The method tableView: titleForHeaderInSection: allows us to specify an optional header value for each section. We just return the letter for this group.

- (NSString *)tableView:(UITableView *)tableView 
titleForHeaderInSection:(NSInteger)section
{
    NSString *key = [keys objectAtIndex:section];
    return key;
}

Build and Run.


Sections ResultA

If we use the Plain instead of Grouped for its Style on the attributes inspector, we get this result.

Sections Result Plain

7.8.4 Adding an Index

With our data, we do not have any problems. But potentially it has. When we have huge number of rows, it's very hard to scroll through. One solution to this problem is to add an index down the right side of the table view. To our surprise, the feature has already been setup and working. The part of code responsible for this is:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    return keys;
}

In this method, the delegate is asking for an array of the values to display in the index. The returned array must have the same number of entries as you have sections, and the values must correspond to the appropriate section. In other words, the first item in this array will take the user to the first section, which is section 0.




I will come back later for this section.



Previous Sections.