This post answers the following questions:
How do I add re-order grips to my UITableViewCells?
What do I do to allow users to remove cells from a UITableView?
How can I implement swipe-to-delete?
In Part II, we covered adding some custom elements to your tables. If you want to allow users to edit the contents of a cell, a common way of doing that is, once the user pushes an “Edit” button, to simply add a UITextField to your cell in the place of where you normally put, say, a UILabel. The code from Part II should be able to get you through this.
But! The UITableView offers some cool built-in features for moving and deleting cells that will give your app a more consistent “iPhone feel” than if you were to custom-implement them yourself, and may save you some time as well.
Starting with our code from the previous tutorial, we’re going to add a member variable to our view controller class header:
NSMutableArray *aCellText;
…and in our viewDidLoad: function, we’ll initialize the array with three objects:
aCellText = [[NSMutableArray alloc] initWithObjects:@"For mobile application", @"development, visit:", @"www.fourtentech.com", nil];
…and don’t forget to release the array when you’re done with it. Then, in cellForRowAtIndexPath:, change this line:
l.text = [NSString stringWithFormat:@"cell %i", indexPath.row];
…to this one:
l.text = [aCellText objectAtIndex:indexPath.row];
Lastly, comment out the UIButton immediately below the line we just added, Build & Go, and you should see something like this:
Our table’s contents is now dynamically generated from an array. Now that we have some dynamic content, we can begin to edit it. Add the following line to your viewDidLoad: function:
tvMain.editing = YES;
…as well as these two delegate methods:
- (BOOL)tableView:(UItableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
NSObject *o = [aCellText objectAtIndex:sourceIndexPath.row];
if(destinationIndexPath.row > sourceIndexPath.row) //moving a row down
for(int x = destinationIndexPath.row; x > sourceIndexPath.row; x--)
[aCellText replaceObjectAtIndex:x-1 withObject:[aCellText objectAtIndex:x]];
else //moving a row up
for(int x = destinationIndexPath.row; x < sourceIndexPath.row; x++)
[aCellText replaceObjectAtIndex:x+1 withObject:[aCellText objectAtIndex:x]];
[aCellText replaceObjectAtIndex:destinationIndexPath.row withObject:o];
[tvMain reloadData];
}
…and you should get the following after Bulid & Go:
We’ve done a lot here — let’s take a step back. The first thing we did was to turn on “editing mode” on the UITableView. Normally, you wouldn’t do this in viewDidLoad:, but rather as a response to a user button press — though for tutorial purposes, this works.
We then added two delegate methods, the first of which is self-explanatory and needs no further commentary, but the second of which is more important. When a user drags a cell from one position to the other using the reorder grips shown in the image, the UI updates, but you also need to update your data storage (in this case, an NSMutableArray) to reflect the change. The code here detects if you’re trying to move a cell upwards or downwards, and then shifts all the cells between your original position and the desired position up or down to accommodate the change. We then reload the table data to ensure that the view contiunes to display properly.
This also provides you a button to delete a cell, but 1) we haven’t yet implemented the code to handle the data store change on deletion, and 2) swipe to delete is way cooler than showing a delete button on every cell. We’ll do both below.
Start by commenting out the tvMain.editing line that we put in earlier to get rid of the grips and delete buttons. We’re then going to let the UITableView know that the number of rows may actually change by updating the contents of numberOfRowsInSection: to read:
return [aCellText count];
Then, we’re going to implement the following delegate method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if(editingStyle == UITableViewCellEditingStyleDelete) {
[aCellText removeObjectAtIndex:indexPath.row];
[tvMain reloadData];
}
}
When this delegate method is defined, the iPhone will allow swipe-to-delete with no further action necessary! A rebuild and a swipe on the middle row will get us: