Friday, June 7, 2013

UITableView moveRowAtIndexPath example in Objective C (iOS).


UITableView moveRowAtIndexPath

Moves the row at a specified location to a destination location.

- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath

Parameters of [UITableView moveRowAtIndexPath]
indexPath
An index path identifying the row to move.
newIndexPath
An index path identifying the row that is the destination of the row at indexPath. The existing row at that location slides up or down to an adjoining index position to make room for it.

Discussion of [UITableView moveRowAtIndexPath]
You can combine row-move operations with row-insertion and row-deletion operations within a beginUpdates–endUpdates block to have all changes occur together as a single animation.

Unlike the row-insertion and row-deletion methods, this method does not take an animation parameter. For rows that are moved, the moved row animates straight from the starting position to the ending position. Also unlike the other methods, this method allows only one row to be moved per call. If you want multiple rows moved, you can call this method repeatedly within a beginUpdates–endUpdates block.

UITableView moveRowAtIndexPath example.
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:newRow inSection:0]; //leaking

[self.tableView beginUpdates];
[self.tableView moveRowAtIndexPath:originalIndexPath toIndexPath:newIndexPath]; //I think this causes the leak
[self.tableView endUpdates];

Example of [UITableView moveRowAtIndexPath].
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
if( fromIndexPath == toIndexPath ) {
    return;
}

if (toIndexPath.row == [self.data count] -1) { //element has been dragged to the bottom
        self.data removeObjectAtIndex:fromIndexPath.row];
        [self performSelector:@selector(delayedReloadData:) withObject:tableView afterDelay:0];
}
else { //Normal reoder operation
        [self.data removeObjectAtIndex:fromIndexPath.row];
     [self.data insertObject:element atIndex:toIndexPath.row];
}
}

- (void)delayedReloadData:(UITableView *)tableView {
//Assert(tableView == self.tableView);
[self.tableView reloadData];
}

UITableView moveRowAtIndexPath example.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {        

    NSUInteger fromIndex = fromIndexPath.row; 
    NSUInteger toIndex = toIndexPath.row;

    if (fromIndex == toIndex) {
        return;
    }

    FFObject *affectedObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:fromIndex]; 
    affectedObject.displayOrderValue = toIndex;

    NSUInteger start, end;
    int delta;

    if (fromIndex < toIndex) {
        // move was down, need to shift up
        delta = -1;
        start = fromIndex + 1;
        end = toIndex;
    } else { // fromIndex > toIndex
        // move was up, need to shift down
        delta = 1;
        start = toIndex;
        end = fromIndex - 1;
    }

    for (NSUInteger i = start; i <= end; i++) {
        FFObject *otherObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:i]; 
        NSLog(@"Updated %@ / %@ from %i to %i", otherObject.name, otherObject.state, otherObject.displayOrderValue, otherObject.displayOrderValue + delta); 
        otherObject.displayOrderValue += delta;
    }

    [self FF_fetchResults]; 
}

End of UITableView moveRowAtIndexPath example article.