I have a situation here and losing my hair...
Very simple application driven by core data which has table view controller with add navigation button. It is very similar to Recipes sample, with couple differences
- There is only single Entity in model with 2 attributes
- The cell in main table is not customized, using default textLabel
The core data part is fine, since the new entry is added to underlying storage and retrieved using fetched results controller. The problem comes when I add new item which will be placed on the top of the list. This can be for instance new item on empty list or item with sort order taking it to the top. The item which is placed on top is not visible! The cell is there, with nil text label, however, I clearly saw the fetched results controller issued update notification and I configured cell, updating with new text.
The only way I can make that cell to be shown is scroll data table to invoke cellForRowAtIndexPath
, then the item text is updated. I add another item which will be placed on top, again the new item text is not visible. I add some item which will be residing on bottom - no problem, the text in cell is visible. Also initially when I just start application there is no problem, all items are visible.
Not sure anyone can help to solve the mistery without the source code, but still hope to get some hints on how to debug... I tried to compare the delegate invocations with Receipe application and all is same.
UPD:
- (void)configureCell:(UITableViewCell *)cell
atIndexPath:(NSIndexPath *)indexPath
{
Word* word = (Word*)[fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = word.word;
printf("configureCell: %d : \"%s\"\n", indexPath.row,
[word.word cStringUsingEncoding:NSASCIIStringEncoding]);
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
printf("didChangeObjectAtRow: %d: insert\n", newIndexPath.row);
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
printf("didChangeObjectAtRow: %d : delete\n", indexPath.row);
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
printf("didChangeObjectAtRow: %d : update\n", indexPath.row);
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
printf("didChangeObjectAtRow: %d / %d : move\n", indexPath.row, newIndexPath.row);
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSInteger count = [[fetchedResultsController sections] count];
if (count == 0)
{
count = 1;
}
printf("number of sections: %d\n", count);
return count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger numberOfRows = 0;
if ([[fetchedResultsController sections] count] > 0)
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
numberOfRows = [sectionInfo numberOfObjects];
}
printf("number of rows in sections: %d is %d\n", section, numberOfRows);
return numberOfRows;
}
- (NSFetchedResultsController*)fetchedResultsController
{
if(fetchedResultsController != nil)
return fetchedResultsController;
// This is autoreleased as the name implies
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Word" inManagedObjectContext:managedObjectContext];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
NSSortDescriptor* sortByWordDescriptor = [[NSSortDescriptor alloc] initWithKey:@"word" ascending:YES];
NSArray* sortArray = [[NSArray alloc] initWithObjects:sortByWordDescriptor, nil];
[fetchRequest setSortDescriptors:sortArray];
NSFetchedResultsController* controller = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"Hmm"];
controller.delegate = self;
self.fetchedResultsController = controller;
[fetchRequest release];
[sortByWordDescriptor release];
[sortArray release];
[controller release];
return fetchedResultsController;
}
- (void) add:(id)sender
{
WordzWordEditView *wordEditViewController = [[WordzWordEditView alloc] initWithNibName:@"WordzWordEditView" bundle:nil];
wordEditViewController.delegate = self;
Word* word = [NSEntityDescription insertNewObjectForEntityForName:@"Word" inManagedObjectContext:self.managedObjectContext];
wordEditViewController.word = word;
UINavigationController *editWordNavigationController = [[UINavigationController alloc] initWithRootViewController:wordEditViewController];
[self presentModalViewController:editWordNavigationController animated:YES];
[wordEditViewController release];
[editWordNavigationController release];
}
UPD: output
controllerWillChangeContent
didChangeObjectAtRow: 0: insert
controllerDidChangeContent
number of sections: 1
number of sections: 1
number of rows in sections: 0 is 2
cellForRowAtIndexPath: 0
configureCell: 0 : "(null)"
post configuring: "(null)"
controllerWillChangeContent
configureCell: 0 : "asd"
didChangeObjectAtRow: 0 : update with 'asd'
controllerDidChangeContent
number of sections: 1
number of sections: 1
number of rows in sections: 0 is 2