Managing the Reordering of Rows
A table view has an editing mode as well as its normal (selection) mode. When a table view goes into editing mode, it displays the editing and reordering controls associated with its rows. The reordering control allows the user to move a row to a different location in the table. As shown in Figure 8-1, the reordering control appears on the right side of the row.

When a table view enters editing mode and when users drag a reordering control, the table view sends a series of messages to its data source and delegate, but only if they implement these methods. These methods allow the data source and delegate to restrict whether and where a row can be moved as well to carry out the actual move operation. The following sections show you how to move rows around in a table view.
What Happens When a Row is Relocated
A table view goes into editing mode when it receives a setEditing:animated: message. Typically (but not necessarily) the message originates as an action message sent when the user taps an Edit button in the navigation bar. In editing mode, a table view displays any reordering (and editing) controls that its delegate has assigned to each row. The delegate assigns the controls in tableView:cellForRowAtIndexPath: by setting the showsReorderControl property of UITableViewCell objects to YES.
When the table view receives setEditing:animated:, it sends the same message to the UITableViewCell object for each visible row. Then it sends a succession of messages to its data source and its delegate (if they implement the methods) as depicted in the diagram in Figure 8-2.

When the table view receives the setEditing:animated: message, it resends the same message to the cell objects corresponding to its visible rows. After that, the sequence of messages is as follows:
The table view sends a
tableView:canMoveRowAtIndexPath:message to its data source (if it implements the method). In this method the delegate may selectively exclude certain rows from showing the reordering control.The user drags a row by its reordering control up or down the table view. As the dragged row hovers over a part of the table view, the underlying row slides downward to show where the destination would be.
Every time the dragged row is over a destination, the table view sends
tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:to its delegate (if it implements the method). In this method the delegate may reject the current destination for the dragged row and specify an alternative one.The table view sends
tableView:moveRowAtIndexPath:toIndexPath:to its data source (if it implements the method). In this method the data source updates the data-model array that is the source of items for the table view, moving the item to a different location in the array.
Examples of Moving a Row
This section comments on some sample code that illustrates the reordering steps enumerated in “What Happens When a Row is Relocated.” Listing 8-1 shows an implementation of tableView:canMoveRowAtIndexPath: that excludes the first row in the table view from being reordered (this row does not have a reordering control).
Listing 8-1 Excluding a row from relocation
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { |
if (indexPath.row == 0) // Don't move the first row |
return NO; |
return YES; |
} |
When the user finishes dragging a row, it slides into its destination in the table view, which sends tableView:moveRowAtIndexPath:toIndexPath: to its data source. Listing 8-2 shows an implementation of this method. Note that it retains the data item fetched from the array (the item to be relocated) because the item is automatically released when it is removed from the array.
Listing 8-2 Updating the data-model array for the relocated row
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath { |
NSString *stringToMove = [[self.reorderingRows objectAtIndex:sourceIndexPath.row] retain]; |
[self.reorderingRows removeObjectAtIndex:sourceIndexPath.row]; |
[self.reorderingRows insertObject:stringToMove atIndex:destinationIndexPath.row]; |
[stringToMove release]; |
} |
The delegate can also retarget the proposed destination for a move to another row by implementing the tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath: method. The following example restricts rows to relocation in their own group and prevents moves to the last row of a group (which is reserved for the add-item placeholder).
Listing 8-3 Retargeting the destination row of a move operation
- (NSIndexPath *)tableView:(UITableView *)tableView |
targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath |
toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath { |
NSDictionary *section = [data objectAtIndex:sourceIndexPath.section]; |
NSUInteger sectionCount = [[section valueForKey:@"content"] count]; |
if (sourceIndexPath.section != proposedDestinationIndexPath.section) { |
NSUInteger rowInSourceSection = |
(sourceIndexPath.section > proposedDestinationIndexPath.section) ? |
0 : sectionCount - 1; |
return [NSIndexPath indexPathForRow:rowInSourceSection inSection:sourceIndexPath.section]; |
} else if (proposedDestinationIndexPath.row >= sectionCount) { |
return [NSIndexPath indexPathForRow:sectionCount - 1 inSection:sourceIndexPath.section]; |
} |
// Allow the proposed destination. |
return proposedDestinationIndexPath; |
} |
© 2012 Apple Inc. All Rights Reserved. (Last updated: 2012-12-13)