I want to use iOS 5 elegant row-movement calls to animate a table view to fit some changes in the state of the model, instead of deleting and pasting the old style.
Changes can include both reordering and in-place updating, and I want to revitalize both, so some lines will require reloadRowsAtIndexPaths.
But! UITableViewit turns out to be simply wrong in handling line overloads when there are moves, if the updated cell shifts the position due to the moves. Using old delete + insert methods, which should be equivalent, works fine.
Here is some code; I apologize for the verbosity, but it compiles and runs. The meat is in the method doMoves:. The exposure is below.
#define THISWORKS
@implementation ScrambledList // extends UITableViewController
{
NSMutableArray *model;
}
- (void)viewDidLoad
{
[super viewDidLoad];
model = [NSMutableArray arrayWithObjects:
@"zero",
@"one",
@"two",
@"three",
@"four",
nil];
[self.navigationItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:
#ifdef THISWORKS
@"\U0001F603"
#else
@"\U0001F4A9"
#endif
style:UIBarButtonItemStylePlain
target:self
action:@selector(doMoves:)]];
}
-(IBAction)doMoves:(id)sender
{
int fromrow = 4, torow = 0, changedrow = 2;
[model replaceObjectAtIndex:changedrow
withObject:[[model objectAtIndex:changedrow] stringByAppendingString:@"\u2032"]];
id tmp = [model objectAtIndex:fromrow];
[model removeObjectAtIndex:fromrow];
[model insertObject:tmp atIndex:torow];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:changedrow inSection:0]]
withRowAnimation:UITableViewRowAnimationRight];
#ifdef THISWORKS
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:fromrow inSection:0]]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:torow inSection:0]]
withRowAnimation:UITableViewRowAnimationAutomatic];
#else // but this doesn't
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:fromrow inSection:0]
toIndexPath:[NSIndexPath indexPathForRow:torow inSection:0]];
#endif
[self.tableView endUpdates];
}
#pragma mark - Table view data source boilerplate, not very interesting
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return model.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@""];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@""];
[cell.textLabel setText:[[model objectAtIndex:indexPath.row] description]];
[cell.detailTextLabel setText:[NSString stringWithFormat:@"this cell was provided for row %d", indexPath.row]];
return cell;
}
What the code does: sets up a tiny model (a small mutable array); when a button is pressed, it makes a small change in the middle element of the list and moves the last element as the first. Then it updates the table view to reflect these changes: reloads the middle row, deletes the last row and inserts a new zero row.
It works. In fact, adding a log in cellForRowAtIndexPathshows that although I ask you to reload row 2, tableview correctly requests row 3 because of the insert when it actually does the update. Hooray!
Now comment out the top #ifdef to use the moveRowAtIndexPath call instead.
tableview 2, 2 (!) -2 ( !). , 1 , , . , moveRowAtIndexPath tableview , "" "" , , . , "" pic , , .



Apple. ? ( , , )? - , ?