Before we get started, you might want to clone the git repo containing the demo Xcode project for this post:
git clone git@github.com:elc/ICB_SectionedTableViewDemo.gitFirst let’s take a look at the UITableViewDataSource protocol methods that we’ll be using to get sections going:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
Instead what we’ll do is build a new dictionary entirely. The keys of this dictionary will correspond to our tableview section titles, and the values for those keys will be arrays containing our dictionary objects; these represent cells in the tableview.
Let’s start by establishing our sections:
BOOL found; // Loop through the books and create our keys for (NSDictionary *book in self.books) { NSString *c = [[book objectForKey:@"title"] substringToIndex:1]; found = NO; for (NSString *str in [self.sections allKeys]) { if ([str isEqualToString:c]) { found = YES; } } if (!found) { [self.sections setValue:[[NSMutableArray alloc] init] forKey:c]; } }
On Intelligence
On The Road
Ishmael
Dune
Your self.sections will be:
‘O’ => empty NSMutableArray
‘I’ => empty NSMutableArray
‘D’ => empty NSMutableArray
Notice that the keys in your NSDictionary aren’t sorted alphabetically. They would be if your initial datasource (self.books) was sorted alphabetically; but there’s no guarantee on that. I’ll show you how to deal with this in a minute.
So the next step is to populate the empty arrays in self.sections with the appropriate NSDictionary objects from self.books:
// Loop again and sort the books into their respective keys for (NSDictionary *book in self.books) { [[self.sections objectForKey:[[book objectForKey:@"title"] substringToIndex:1]] addObject:book]; }
// Sort each section array for (NSString *key in [self.sections allKeys]) { [[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES]]]; }
At this point you’ll probably want to drop this in too:
[self.tableView reloadData];
#pragma mark - #pragma mark Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[self.sections allKeys] count]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return [[[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:section]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:section]] count]; } - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { return [[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; }
OK. So now that we have sections out of the way, let’s show some cells…
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; } NSDictionary *book = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row]; cell.textLabel.text = [book objectForKey:@"title"]; cell.detailTextLabel.text = [book objectForKey:@"description"]; return cell; }