Search code examples
ioscore-datansfetchedresultscontrollernsfetchrequest

NSFetchedResultsController trying to limit number of records displayed


When creating a NSFetchRequest to pass over to my NSFetchedResultsController I am setting the fetchLimit property to 3.

Now initially this appears to work fine. I can modify the first three returned objects in any way which changes their order and they all reshuffle correctly. The problem appears when I either alter a object which initially fell outside the first three in a way which now brings it into the first three, or when simply adding a new object so it will appear within the first three.

What I expected to happen: Inserted object pushes the rest down and one drops off the bottom.

What actually happens: Inserted object pushes the rest down and the record count grows to 4?!

Can anyone explain this, or how I should deal with this?


Solution

  • I have made some progress working around this, basically by ignoring numberOfObjects and returning the actual length I want the table fixed at. This takes a bit of trickery in controller:didChangeObject:... but seems to be working so far.

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return kTableSize;
        //return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
    }
    
    - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
    {
        UITableView *tableView = self.myTableView;
    
        switch(type) {
    
            case NSFetchedResultsChangeInsert:
    
                // Only modify table if insert will effect visible rows
                if (newIndexPath.row < kTableSize) {
                    // Delete last row to maintain fixed length
                    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:(kTableSize - 1) inSection:newIndexPath.section]] withRowAnimation:UITableViewRowAnimationAutomatic];
    
                    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
                }
                break;
    
            case NSFetchedResultsChangeDelete:
    
                // Only modify table if delete will effect visible rows
                if (indexPath.row < kTableSize) {
                    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    
                    // Insert extra row to maintain fixed length
                    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:(kTableSize - 1) inSection:newIndexPath.section]] withRowAnimation:UITableViewRowAnimationAutomatic];
                }
                break;
    
            case NSFetchedResultsChangeUpdate:
    
                // Only modify table if update will effect visible rows
                if (indexPath.row < kTableSize) {
                    [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
                }
                break;
    
            case NSFetchedResultsChangeMove:
    
                // Only modify table if move will effect visible rows
                if ((indexPath.row < kTableSize) || (newIndexPath.row < kTableSize)) {
    
    
                    // Delete old row or last row of table
                    if (indexPath.row < kTableSize) {
                        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
                    } else {
                        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:(kTableSize - 1) inSection:newIndexPath.section]] withRowAnimation:UITableViewRowAnimationAutomatic];
                    }
    
                    // Insert new row or a row at bottom of table
                    if (newIndexPath.row < kTableSize) {
                        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
                    } else {
                        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:(kTableSize - 1) inSection:newIndexPath.section]] withRowAnimation:UITableViewRowAnimationAutomatic];
                    }
                }
                break;
        }
    }
    

    Also need to take care in tableView:cellForRowAtIndexPath: to make sure we don't try and access an object which doesn't exist if there are less objects than the table length.