0

I have a table view with more than 1000 cells (I display about 10 on the screen). Following a "filter" action by the user I want to remove about 80% of them, then after a second filter another 80%. When working with 100 cells it's roughly instantaneous but with a 1000 it takes 0.5 seconds to finish the method [tableView endUpdates] and this totally destroys the user experience.

I create the array of Index Paths like this:

for (int i=1; i<[appDelegate.myArray count]; i++) {   //more than 1000 objects in myArray
    if (![[appDelegate.myArray objectAtIndex:i] myTest]) {  //myTest returns a BOOL 
        [appDelegate.rowsToDelete addObject:[NSIndexPath xxxxx]]; //the index path of the object that just failed the test and must be removed];
 }
}
[self.myTableViewController.tableView deleteRowsAtIndexPaths:appDelegate.rowsToDelete withRowAnimation:UITableViewRowAnimationRight];

I have tried 2 solutions, one is threading so that my user can see other parts of the UI moving whist the tableView is updating. But if he launches another level of filtering this often results in the long wait if not a crash.

I have also tried to remove the first few cells (those on the screen) and then just remove the other objects that have failed the test from the datasource without removing their corresponding cells from the tableView as they are out of sight. This gives the good, rapid result, but I then get problems with the number of rows such as:

'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (94) must be equal to the number of rows contained in that section before the update (927), plus or minus the number of rows inserted or deleted from that section (0 inserted, 215 deleted).'

Has anybody had such a performance issue? ways to solve it? Cheers, CD

3 Answers3

0

I agree with @futureelite7 that you should reload the dataSource rather than manipulating the view. I proposed a solution to a similar question here.

The basic idea is to call reloadSections:withRowAnimation: and in your UITableViewDataSource methods switch on the segmented control's selectedSegmentIndex.

Assuming your data is flat (only one section) it would look something like this:

- (IBAction)segmentSwitch:(id)sender
{
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    switch (self.segmentedControl.selectedSegmentIndex)
    {
        default:
        case 0:
            return [self.allRows count];
        case 1:
            return [self.onlySomeRows count];
    }
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    id data;
    switch (self.segmentedControl.selectedSegmentIndex)
    {
        default:
        case 0:
            data = [self.allRows objectAtIndex:[indexPath row]];
            break;
        case 1:
            data = [self.onlySomeRows objectAtIndex:[indexPath row]];
            break;
    }

    //TODO: use data to populate and return a UITableViewCell...
}
Community
  • 1
  • 1
0

Try caching properties and using fast enumeration for your loop:

//int i = 0;
NSMutableArray *rowsToDelete = appDelegate.rowsToDelete;
for(id current in appDelegate.myArray) {
    if([current myTest]) [rowsToDelete addObject:[NSIndexPath ...]];
    //++i;
}
[self.myTableViewController.tableView deleteRowsAtIndexPaths:rowsToDelete withRowAnimation:UITableViewRowAnimationRight];

If you use the index of the item to create the NSIndexPath, uncomment the first and fifth lines. Also, i noticed that in your code you start at i=1. Since NSArrays start at 0, this would never delete the first cell. If you actually want this, change i = 0 to i = 1.

ughoavgfhw
  • 39,734
  • 6
  • 101
  • 123
  • Hi,thanks! You allowed me to go down from 150ms to 100ms. Nevertheless, I put NSLog checkpoint to break down to timing and discovered that the method taking time (500ms) was the one performing the deletion: [self.myTableViewController.tableView deleteRowsAtIndexPaths:rowsToDelete withRowAnimation:UITableViewRowAnimationRight]; Going though the array only makes up for 20% of total time. The thing is I am deleting rows that are way out of sight, but have to do so to avoid rows:insection: Exceptions. Any idea how to speed up deletion or avoid doing it (altering datasource?) – – user543867 Dec 19 '10 at 17:24
  • Sorry, but not that I know of. – ughoavgfhw Dec 19 '10 at 19:54
0

You might just want to do the filtering and reload the entire table view instead, using the new number of rows. There's no point in doing all those deletion animation if they end up out of sight anyway. Or just figure out which part is visible, then load and delete only those rows that are currently visible to the user?

futureelite7
  • 11,462
  • 10
  • 53
  • 87