UITableView – Sectioned Table View

Sometimes it makes sense to group information and show it to the user. In this tutorial, you will learn how to create a simple sectioned table view. Click on “Read more” to learn more…

Introduction
So far we have seen how easy it is to display list of items in a table view. As it turns out, displaying grouped items is easy too. This is the third tutorial in the UITableView tutorial series and it borrows its source code from the previous one. This is how the final app will look like

Preparing the Data Source
From the above picture it is obvious that the countries are grouped into two sections: “Countries to visit” and “Countries visited”. I don’t know where I want to go next but Iceland is been on my list for a long time :).

Since the information is now grouped, we need to create our data source in a specific way, which will make it easier to display the data. We will still use a NSMutableArray to hold the data. This time we will populate it with two dictionary objects, instead of simple strings. The dictionary objects will contain one key/value pair, where the key will be a string and the value will be an array containing all the countries. This is done in viewDidLoad method and this is how the method changes from the second tutorial

//RootViewController.m
- (void)viewDidLoad {
[super viewDidLoad];

//Initialize the array.
listOfItems = [[NSMutableArray alloc] init];

NSArray *countriesToLiveInArray = [NSArray arrayWithObjects:@"Iceland", @"Greenland", @"Switzerland", @"Norway", @"New Zealand", @"Greece", @"Rome", @"Ireland", nil];
NSDictionary *countriesToLiveInDict = [NSDictionary dictionaryWithObject:countriesToLiveInArray forKey:@"Countries"];

NSArray *countriesLivedInArray = [NSArray arrayWithObjects:@"India", @"U.S.A", nil];
NSDictionary *countriesLivedInDict = [NSDictionary dictionaryWithObject:countriesLivedInArray forKey:@"Countries"];

[listOfItems addObject:countriesToLiveInDict];
[listOfItems addObject:countriesLivedInDict];

//Set the title
self.navigationItem.title = @"Countries";
}

P.S The array is declared in the header file and released in the dealloc method, this is shown in the first part of the tutorial. It also contains information about the detail view.

We create two arrays: countries to visit and countries visited. The arrays are then used to create dictionary objects with the key “Countries” and they are added to the “listOfItems” array with countries to live followed by countries lived in.

Preparing UITableView to display data

The table view needs to know how many sections it should expect, which is the number returned in numberOfSectionsInTableView method. Since we only have two dictionary objects added to the array, it know to display two sections. This is how the code looks like

//RootViewController.m
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

return [listOfItems count];
}

The table view needs to know how many rows it should expect it in every section, which is the number returned in tableView:numberOfRowsInSection method. This is how the code looks like

//RootViewController.m
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

//Number of rows it should expect should be based on the section
NSDictionary *dictionary = [listOfItems objectAtIndex:section];
NSArray *array = [dictionary objectForKey:@"Countries"];
return [array count];
}

We have added two dictionary objects in the array, so this method will be called twice. Since the index of the section and the array starts with 0, We can use this to get the dictionary located at a specific index. Once we find out which dictionary the table view is displaying, we get the array using the key “Countries” and return the count of the array.

Displaying the header text
We took care of the number of sections and the rows the table view should know about. We still have to display the actual header text that shows up in the table view. This is done in tableView:titleForHeaderInSection method which is called twice since we only have two groups. This is how the code looks like

//RootViewController.m
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

if(section == 0)
return @"Countries to visit";
else
return @"Countries visited";
}

We know that the first dictionary in “listOfItems” array contains the list of countries I want to visit. So the above code checks if the section is 0 or not and displays the relevant header text.

Displaying the text of the UITableViewCell
We now have the header text but what about the text of the cell. The logic is similar to that of tableView:numberOfRowsInSection. We first find out which dictionary object we should read, from the section property of NSIndexPath. We then get the dictionary object, with its array from the key “Countries”. At last we display the text of the cell by getting the string at a given row, which comes from the row property of NSIndexPath. This is how the code looks like

//RootViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}

// Set up the cell...

//First get the dictionary object
NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:@"Countries"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.text = cellValue;

return cell;
}

Selecting the right row
The method tableView:didSelectRowAtIndexPath also changes, to adjust the sectioned table view. This is how the code looks like

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

//Get the selected country

NSDictionary *dictionary = [listOfItems objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:@"Countries"];
NSString *selectedCountry = [array objectAtIndex:indexPath.row];

//Initialize the detail view controller and display it.
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
dvController.selectedCountry = selectedCountry;
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
dvController = nil;
}

The code is similar to tableView:cellForRowAtIndexPath, in how we access the dictionary, array, and the selected country value.

Run your application to see the sectioned table view in action.

Setting the style of UITableView
If your app looks like the picture above, then the style of the table view is set to “Plan” in Interface Builder. Change this to “Grouped” and see how the display changes when you run the app.

Conclusion
We have seen how we can display some information which is grouped together. I hope this tutorial was helpful to you and please leave your comments. My next tutorial will show you how to search the UITableView using the UISearchBar control.

Happy Programming,
iPhone SDK Articles


Attachments

Suggested Readings