Custom cell in empty table with NSFetchedResultsController

I am trying to place a custom empty cell when my UITableView is empty.

I am using the following code:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { int count = [self.fetchedResultsController fetchedObjects].count; if (count == 0) { return 1; } return count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell * cell = nil; int count = [self.fetchedResultsController fetchedObjects].count; if (count == 0) { // return empty cell cell = [self getEmptyCellOfTableView:tableView cellForRowAtIndexPath:indexPath]; } else { cell = [self getMyQuestionCellOfTableView:tableView cellForRowAtIndexPath:indexPath]; } return cell; } 

FRC didChangeObject :

 - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeMove: [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } } 

This works fine, the problem is fetchedObject.count> 0 , than I get the following error:

 CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to - controllerDidChangeContent: Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null) 

I understand that this is due to the fact that I inserted 1 new line and the number will remain at 1 instead of 2.

How can I fix this to fit my behavior?

+4
source share
2 answers

After updating the table view, the value returned by numberOfRowsInSection must exactly match the previous number of rows plus the number of rows inserted minus the number of rows deleted.

In your case, when the first object is inserted, in the FRC delegate method, insertRowsAtIndexPaths is called for the new object, but instead of displaying an additional row, the "empty" cell is replaced by another cell, so the number of rows is the same anyway.

It should work if you change the delegate method of FRC controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: as follows:

 case NSFetchedResultsChangeInsert: if ([[self.fetchedResultsController fetchedObjects] count] == 1) { // First object inserted, "empty cell" is replaced by "object cell" [tableView reloadRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; } else { [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; } break; case NSFetchedResultsChangeDelete: if ([[self.fetchedResultsController fetchedObjects] count] == 0) { // Last object removed, "object cell" is replaced by "empty cell" [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else { [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } break; 
+12
source

PSA: If you have not tested Yang Meyer's solution for displaying a cell on an empty table, I suggest you do it. This is a smart and much simpler way to handle an empty table or an empty NSFetchedResultsController.

Jan Meyer Best Practice for Processing Empty Tables

The way I used it would be in my viewDidLoad , I would assume that the results were empty. Only when my fetchedResultsController objects had objects can I change the data source.

FJFetchedDataController.h

 @interface FJFetchedDataTableController : UITableViewController <NSFetchedResultsControllerDelegate, UITableViewDelegate> @end 

FJFetchedDataController.m

 @implementation FJFetchedDataTableController { @private FJEmptyDataSource *_emptyDataSource; }; - (void)viewDidLoad { [super viewDidLoad]; // create the empty data source _emptyDataSource = [[FJEmptyDataSource alloc] init]; // point the tableView to the empty data source self.tableView.dataSource = _emptyDataSource; self.fetchedResultsController.delegate = self; // TODO: configure your fetchedResultsController here NSError *error; if (![self.fetchedResultsController performFetch:&error]) { // TODO: error handling } if ([self.fetchedResultsController.fetchedObjects count]) { // set the table source back to itself (or whatever you want as the data source) // since we know we have results self.tableView.dataSource = self; return; } } 

Simple but really effective.

+1
source

Source: https://habr.com/ru/post/1492458/


All Articles