Editing a UITableView: The Animated Insert Row

September 2, 2009

This post originally appeared on my Incomplete Labs blog on September 2nd, 2009.


An iPhone app I’m working on has your traditional table view with an edit button in the upper right. I wanted an “Add New Item” row to appear at the bottom of the table once the edit button was tapped (think iPhone contact editor), but I couldn’t find a succinct description of how that’s done. Here’s how I did it.

If you start out with a new UITableViewController subclass, Apple’s template code for the UITableViewController sets us up with what we need to enable the edit button: (it’s commented out in -viewDidLoad)

self.navigationItem.rightBarButtonItem = self.editButtonItem;

If we uncomment this line the newfound edit button will animate our table into a listing of (apparently) deletable rows. What we need is a way to add a row to the end of the section that we want the user to be able to add to. Initially I tried doing this by checking for -isEditing inside -tableView:numberOfRowsInSection:, hoping that the table data would be automagically reloaded when Edit was tapped (it wasn’t):

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
    if ([tableView isEditing])
        return [items count] + 1;
    else
        return [items count];
}

I also added code to -tableView:cellForRowAtIndexPath: to provide the a cell label for the extra row (“Add an item”), also implementing -tableView:editingStyleForRowAtIndexPath: to return UITableViewCellEditingStyleInsert as appropriate. Those steps are important, but they weren’t enough. My data source and delegate weren’t being queried for the updated information and the table view wasn’t showing the new row when the edit button was pressed.

What we need is a way to tell the UITableView that there is a new row, and we need to tell it at the right time. I was tempted to look at setting up my own target/action on the Edit button, but since we’re subclassing UITableViewController the -setEditing:animated: method is a great place to do this. We can just override it in our own subclass, being sure to call the superclass’s implementation:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated:animated];

Then we call the table view’s -insertRowsAtIndexPaths:withRowAnimation: to tell the table view where the row is being added when we’re editing. There’s a complementary call, -deleteRowsAtIndexPaths:withRowAnimation:, which will work for when we aren’t editing any longer:

    NSArray *paths = [NSArray arrayWithObject:
						[NSIndexPath indexPathForRow:1 inSection:0]];
    if (editing)
    {
        [[self tableView] insertRowsAtIndexPaths:paths 
								withRowAnimation:UITableViewRowAnimationTop];
    }
    else {
        [[self tableView] deleteRowsAtIndexPaths:paths 
								withRowAnimation:UITableViewRowAnimationTop];
    }
}

That’s about it. -insertRowsAtIndexPaths:withRowAnimation: and its complement are the key to getting the behavior we want, and we can use them in other scenarios to get smooth table view updates.