How to prevent NSFetchedResultsController from updating tableview when the controller disappears?
Asked Answered
M

4

7

I have a UITabbar with multiple controllers in it. One of the controllers is used to add Events to Core Data, while another controller is used to display events as in a UITableView using NSFetchedResultsController.

Here's the behaviour that I would like to achieve: Upon disappearing, the UITableView stops updating, and when the user comes back, the entire table view is reloaded. Otherwise, inserting events from the other controller takes longer, as new rows are created in the UITableView, even thought it is not visible.

I'm wondering how I can achieve this behavior, as it doesn't seem to work as I expect it would be:

I have set the delegate of the NSFetchedResultsController to nil in viewWillDisappear, and restore it in viewWillAppear, along with a call to [UITableView reloadData];

Somehow, I do not see the new data, and suspect this is due to the way NSFetchedResultsController stops fetching if it does not have a delegate.

How can I properly "suspend" updates to UITableView when it disappears, but still able to see the entire dataset when the controller reappears?

Messuage answered 1/11, 2012 at 23:35 Comment(0)
I
7

Try sending performFetch: to the NSFetchedResultsController in viewWillAppear: after you have set its delegate back to self.

Inhabitant answered 2/11, 2012 at 0:9 Comment(0)
Q
2

I think you do not have to "suspend" the table view updates. A UITableView will anyway only request data from the NSFetchedResultsController for visible cells. If the the table view is not visible, no updates will be fired.

Did you test if inserting events from another controller really takes longer? I doubt it. What does Instruments say?

If your delegate methods are fired, you could still check if the table view is visible before doing any updates.

After that, you do exactly as suggested by rob: do a performFetch: in viewWillAppear:.

Querist answered 2/11, 2012 at 11:37 Comment(2)
Even if there's no real UI updates, beginUpdates at controllerWillChangeContent and endUpdates at controllerDidChangeContent are "freezing" FRC thread for some time.Microeconomics
I'm suffering these freezes. How can I prevent it? My FRC is performing 5k updates and freezing my app for 30 secondsFrightful
O
0

Instead of setting the delegate of the NSFetchedResultsController to nil in viewWillDisappear, try setting an object of NSFetchedResultsController to nil

Overlap answered 2/11, 2012 at 0:6 Comment(0)
A
0

What about this crude approach? Not tested it.

@property (nonatomic) BOOL bruteForceReload;

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.bruteForceReload = NO;
}

-(void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    self.bruteForceReload = YES;
}

-(void)setBruteForceReload:(BOOL)bruteForceReload {
    _bruteForceReload = bruteForceReload;
    if (_bruteForceReload) {
        [self.tableView reloadData];
    }
}

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    if (!self.bruteForceReload) {
        [self.tableView beginUpdates];
    }
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    if (!self.bruteForceReload) {
        switch(type) {
            case NSFetchedResultsChangeInsert:
                [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeDelete:
                [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
                break;

            default:
                return;
        }
    }
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{
    if (!self.bruteForceReload) {
        UITableView *tableView = self.tableView;

        switch(type) {
            case NSFetchedResultsChangeInsert:
                [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeDelete:
                [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeUpdate:
                [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
                break;

            case NSFetchedResultsChangeMove:
                [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
                [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;
        }
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    if (!self.bruteForceReload) {
        [self.tableView endUpdates];
    } else {
        [self.tableView reloadData];
    }
}
Assemblyman answered 30/9, 2015 at 16:53 Comment(1)
if the view isn't on screen it would be better to turn off the heavy-weight change tracking of the fetch controllerBessette

© 2022 - 2024 — McMap. All rights reserved.