For a simple example of using a NSMutableArray of strings called rows, what do I have to implement in my table controller to move the tableView rows and have the changes reflect
NSMutableArray
has a method called exchangeObjectAtIndex:withObjectAtIndex:
.
Neither of the above will work. In the original posted code, the tableview will crash because it removes the data that will still be in use, and even after that was corrected, The array would not have the correct data due to the way table does a 'rearrange'.
exchangeObjectAtIndex:withObjectAtIndex: will not work for rearranging an array according to how a tableview implements its own rearranging
Why? Because when the user selects a table cell to rearrange it, that cell does NOT get swapped with the cell they are moving it to. The cell they selected gets inserted at the new row index and then the original cell is removed. At least that's the way it appears to the user.
Solution: Due to the way the tableview implements a rearrange, we need to perform a check to make sure we add and remove the right row. This code I put together is simple and works perfect for me.
Using the original posted code data as an example:
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
NSLog(@"move from:%d to:%d", fromIndexPath.row, toIndexPath.row);
// fetch the object at the row being moved
NSString *r = [rows objectAtIndex:fromIndexPath.row];
// checks to make sure we add and remove the right rows
if (fromIndexPath.row > toIndexPath.row) {
// insert the object at the target row
[rows insertObject:r atIndex:toIndexPath.row];
// remove the original from the data structure
[rows removeObjectAtIndex:(fromIndexPath.row + 1)];
}
else if (fromIndexPath.row < toIndexPath.row) {
// insert the object at the target row
[rows insertObject:r atIndex:(toIndexPath.row + 1)];
// remove the original from the data structure
[rows removeObjectAtIndex:(fromIndexPath.row)];
}
}
If you take a few moments and take a look at what happens to a tableview during a rearrange you will understand why we add the 1's where we did.
I'm pretty new to xcode, so I know there is probably an easier way to do this, or the code can probably be simplified....Just trying to help out where I can because it's taken me a few hours to figure this out. Hope this saves someone some time!
Here we do our heavy lifting.
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
NSLog(@"move from:%d to:%d", fromIndexPath.row, toIndexPath.row);
// fetch the object at the row being moved
NSString *r = [rows objectAtIndex:fromIndexPath.row];
// remove the original from the data structure
[rows removeObjectAtIndex:fromIndexPath.row];
// insert the object at the target row
[rows insertObject:r atIndex:toIndexPath.row];
NSLog(@"result of move :\n%@", [self rows]);
}
Since this is a basic example, lets make all the rows moveable.
- (BOOL)tableView:(UITableView *)tableView
canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
According to Apple's documentation, and my own experience, this is some simple code that works quite well:
NSObject *tempObj = [[self.rows objectAtIndex:fromIndexPath.row] retain];
[self.rows removeObjectAtIndex:fromIndexPath.row];
[self.rows insertObject:tempObj atIndex:toIndexPath.row];
[tempObj release];
Found this discussion after breaking my head against a simple implementation... Michael Berding solution is the best for me, the Apple way, just remember to remove retain and release when using ARC. So, a more concise solution
NSObject *tempObj = [self.rows objectAtIndex:fromIndexPath.row];
[self.rows removeObjectAtIndex:fromIndexPath.row];
[self.rows insertObject:tempObj atIndex:toIndexPath.row];