This is possible to do in a fairly clean way.
I'm assuming you're starting with a standard tableview set up with a standard NSFetchResultsController that uses Apple's sample code.
First you need two utility functions:
- (NSIndexPath *)mapIndexPathFromFetchResultsController:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
indexPath = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
return indexPath;
}
- (NSIndexPath *)mapIndexPathToFetchResultsController:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
indexPath = [NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section];
return indexPath;
}
These should be fairly self explanatory - they're just helpers to deal with adding the extra row when we want to use an index path from the fetched results controllers to access the table, or removing it when going the other way.
Then we need to create the extra cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"MyCellId";
if (indexPath.section == 0 && indexPath.row == 0)
{
UITableViewCell *cell;
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.textLabel.text = NSLocalizedString(@"Extra cell text", nil);
return cell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
make sure we configure it correctly (configurecell will only be called for cells from the fetch results controller):
// the indexPath parameter here is the one for the table; ie. it's offset from the fetched result controller's indexes
- (void)configureCell:(SyncListViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
indexPath = [self mapIndexPathToFetchResultsController:indexPath];
id *obj = [fetchedResultsController objectAtIndexPath:indexPath];
<... perform normal cell setup ...>
}
and tell the tableview it exists:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger numberOfRows = 0;
if ([[fetchedResultsController sections] count] > 0) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
numberOfRows = [sectionInfo numberOfObjects];
}
if (section == 0)
numberOfRows++;
return numberOfRows;
}
and respond to selection:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (indexPath.section == 0 && indexPath.row == 0)
{
[self doExtraAction];
return;
}
... deal with selection for other cells ...
and then remap any updates we get from the results controller:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
indexPath = [self mapIndexPathFromFetchResultsController:indexPath];
newIndexPath = [self mapIndexPathFromFetchResultsController:newIndexPath];
switch(type) {
... handle as normal ...