Animated Sorting in UITableView

No Comments

Let me share another rather simple but neat trick which is quite useful when you need to sort a list of items visually represented by UITableView. Let’s say we have an NSArray holding a list of cities stored in arbitrary order. And in your app you’d like to offer an option to sort the list in one or the other way.

Sorting as such is very easy to implement since NSArray class already offers a number of methods (sortedArrayUsing…) which allow you to get the sorted version of the array by passing one of the following: NSSortDescriptor, comparator block, sorting C function or selector of the sorting method. To update the table view, you would then probably call [self.tableView reloadData].

That works just fine but wouldn’t it be better to provide nice animated feedback to user by moving table view cells into right slots? This is almost just as easy. The key to the solution is to use batch update mechanism built right into UITableView allowing to apply multiple changes all at once. The animation of the table view cells would be then achieved by already available [UITableView moveRotAtIndexPath:toIndexPath:]. We just need to calculate source and destination index paths for each cell which needs to be moved since the underlying data element changed it’s position during sorting operation.

Let’s have a look at the code:

- (IBAction)sort {
 
    // We need an unsorted copy of the array for the animation
    NSArray *unsortedList = [self.cities copy];
 
    // Sort the elements and replace the array used by the data source with the sorted ones
    self.cities = [self.cities sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        return [obj1 compare:obj2];
    }];
 
    // Prepare table for the animations batch
    [self.tableView beginUpdates];
 
    // Move the cells around
    NSInteger sourceRow = 0;
    for (NSString *city in unsortedList) {
        NSInteger destRow = [self.cities indexOfObject:city];
 
        if (destRow != sourceRow) {
            // Move the rows within the table view
            NSIndexPath *sourceIndexPath = [NSIndexPath indexPathForItem:sourceRow inSection:0];
            NSIndexPath *destIndexPath = [NSIndexPath indexPathForItem:destRow inSection:0];
            [self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:destIndexPath];
 
        }
        sourceRow++;
    }
 
    // Commit animations
    [self.tableView endUpdates];
}

I hope you enjoyed this example. You can download the full sample project here: AnimatedTableSorting. Take care!

Animated resizing of UITableView header

No Comments

Other day i was working on a project in which we had a UITableViewController configured with a UITableView in which tableHeaderView property would be set to a custom UIView reference. The custom view is then used to display variable amount of information and it was required that it shrinks when the content in it becomes less. So, i tried a few things and it turned out that UITableView considers the header view for the layout calculations only once you set it. When you change a frame of the header view, the UITableView is not getting aware of this.

The solution to the described problem was then rather simple: just put the tableHeaderView property assignment operation inside of the animation block in which you would change the frame of the header view itself. Then Core Animation thread will set the property, every time, frame is adjusted. Something like that:

// Calculate new frame size for the table header
CGRect newRect = ...;
// Get the reference to the header view
UIView *tblHeaderView = self.tableView.tableHeaderView;
// Animate the height change
[UIView animateWithDuration:0.5 animations:^{
    tblHeaderView.frame = newRect;
    self.tableView.tableHeaderView = tblHeaderView;
}];

Typical scenario would be to put an Ad banner into the UITableView’s header and then animate it away when it becomes unavailable or so. The initial problem i had did cost some research, so i thought it would be worth sharing. Hope you’ll find it useful.