Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

animating a large number of rows/sections in UITableView poor performance

we're not talking thousands of rows or anything, although if there was a way to make things scale up that high, I'd love it.

I have a table with 27 sections and 180 rows spread across all sections, and the scenario I'm currently getting stuck in is when I animate things to a model state with only 3 sections and 5 rows, and (even worse) back again.

I'm batching all the animations with beginUpdates/endUpdates. My app pretty well locks up for 1-2 seconds on an iphone4 while it figures things out, then the animations start.

I've tried both animating the removal/addition of each row, keeping the sections around (and dropping their row counts to 0 in the removal case), and also animating just the removal/insertions of the sections themselves (when the row count would have dropped to 0). I would have assumed the latter would give better performance but it didn't change things at all.

Is there anything that can be done on the app end to speed this up? Right now I have a rather gross bit of code to bail out of the individual animations if there are more than 20 of them, opting to just reloadData instead.

edit here's code that exhibits the problem. The performance of this code is slightly better than the equivalent monotouch code (which is what I was using before), but it's still pretty bad.

#import "TableViewController.h"

@interface MyTableViewDataSource : NSObject<UITableViewDataSource> {
    int rows;
};

@end

@implementation MyTableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (void)setRowCount:(int)r
{
    rows = r;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return rows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

    cell.textLabel.text = [NSString stringWithFormat:@"row %d", indexPath.row];

    return cell;
}

@end

@implementation MyTableViewController {
    UIBarButtonItem *populateButtonItem;
};

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        populateButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Populate" style:UIBarButtonItemStylePlain target:self action:@selector(populateDataSource)];
    }
    return self;
}

- (void)populateDataSource
{
    NSMutableArray* new_rows = [[NSMutableArray alloc] init];
    [((MyTableViewDataSource*)self.tableView.dataSource) setRowCount:200];

    for (int i = 0; i < 200; i ++)
        [new_rows addObject:[NSIndexPath indexPathForRow:i inSection:0]];

    [self.tableView beginUpdates];
    [self.tableView insertRowsAtIndexPaths:new_rows withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.tableView endUpdates];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.tableView.dataSource = [[MyTableViewDataSource alloc] init];
    self.navigationItem.rightBarButtonItem = populateButtonItem;
}

@end
like image 396
toshok Avatar asked May 06 '12 22:05

toshok


1 Answers

It only makes sense to animate the rows that are visible. Instead of doing the animation for all the rows that you're inserting, consider just animating the insertion of those rows that will be visible.

Also, are you sure that it's the animation that's causing the delay? Do you get the same delay if you pass UITableViewRowAnimationNone for the animation, or is it faster? If it's faster, then again, avoid animating those insertions that won't be visible. (You can figure out which rows are currently visible using -indexPathsForVisibleRows.) If it's not faster, then the problem is probably not related to the animation at all, but rather the overhead of inserting a few hundred rows all at once. Reloading the entire table as you're now doing is one option; inserting the rows in smaller batches is another.

Finally, it'd be a good idea to profile your app with Instruments while you do the insertion. You'll get a better idea of what the app is doing during that delay, and that's the first step toward eliminating the delay.

like image 157
Caleb Avatar answered Oct 07 '22 07:10

Caleb