NSFetchResultController Delegate Error - iphone

I have a couple of UIViewControllers that show a table view of my 'User' objects as the section, and 'Site' objects as rows under each section. The Site to User relationship is one-to-many.
At the moment I am showing this data with the following code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:0];
return [sectionInfo numberOfObjects];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
User *sectionUser = (User*)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:section inSection:0]];
return [[sectionUser.sitesToUser allObjects] count];
}
In my NSFetchResultsController I'm just pulling all User objects, nothing special.
Now my issue - whenever I update the results, by removing a section (ie a User) or adding a User whilst this view is also open, I get an NSFetchedResultsController delegate error:
Serious application error. An exception was caught from the delegate of
NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid
update: invalid number of sections. The number of sections contained in the table
view after the update (2) must be equal to the number of sections contained in the
table view before the update (1), plus or minus the number of sections inserted or
deleted (0 inserted, 0 deleted). with userInfo (null)
I only get it in the views where I am displaying my data in this way, is User is section, Site/s are rows under each section.
Am I doing something really stupid? I thought it would just remove the section when I delete it etc... Seems to work fine if each User is a ROW in the UITableView - which I believe is the crux of my issue...

I'm using Core Data but my code for numberOfSectionsInTableView: and tableView:numberOfRowsInSection: method is different... I don't know if this can solve your problem but, try to use this code:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rowsNumber = 0;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
rowsNumber = [sectionInfo numberOfObjects];
return rowsNumber;
}
For the sections, I've never used it but try to work on the sectionNameKeyPath argument of the initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName: when you initialize your controller.

You haven't shown your fetchedResultsController delegate calls:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
- (void)controller:didChangeObject:atIndexPath:forChangeType:newIndexPath
- (void)controller:didChangeSection:sectionInfoAtIndex:forChangeType:
These calls are responsible for adding and removing the rows / sections from your table so everything "adds up" as far as the tableview is concerned.
In your case you are kind of "abusing" what a section is so this code could get tricky.
If you are prepared to do without animation you could just implement:
-(void)controllerDidChangeContent:(NSFetchedResultsController*) controller {
[self.tableView reloadData];
}
Daniel.

Related

ios 5: Invalid update: invalid number of rows in section

I only have this problems with iOS 5, no problems with iOS 6
This is my log
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 3. The number of rows contained in an existing section after the update (3) 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 (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
And my code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[dictionary allKeys] count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSArray *keyArray = [dictionary allKeys];
return [[dictionary objectForKey:[keyArray objectAtIndex:section]] count];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
= if (editingStyle == UITableViewCellEditingStyleDelete)
{
//First get all the keys of dictionary into one array
NSArray *sectionsArray = [dictionary allKeys];
//Get all the data of tapped section into one array by using indexpath.section
NSMutableArray *objectsAtSection = [dictionary objectForKey:[sectionsArray objectAtIndex:indexPath.section]];
//remove the particular object by using indexPath.row from the array
[objectsAtSection removeObjectAtIndex:indexPath.row];
// Update dictionary
[table beginUpdates];
// Either delete some rows within a section (leaving at least one) or the entire section.
if ([objectsAtSection count] > 0)
{
[dictionary setObject:objectsAtSection forKey:[sectionsArray objectAtIndex:indexPath.section]];
// Section is not yet empty, so delete only the current row.
// Delete row using the cool literal version of [NSArray arrayWithObject:indexPath]
[table deleteRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}else{
[dictionary removeObjectForKey:[sectionsArray objectAtIndex:indexPath.section]];
// Section is now completely empty, so delete the entire section.
[table deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
}
[table endUpdates];
}
}
in iOS 5, after delete some row, and some section, i have this problems.
Could you please help me?
Need to add
[self.tableView reloadData];
and
self.editing = YES;
This is needed because the table view doesn't initially have information about its data source and delegate; if you create a table view, it always needs to be sent a reloadData message as part of its initialization.
Hope it will Help you
Ok, it seems I found an answer. See this SO question and answer, explaining why using allKeys method is bad.
As long as you don't add or remove any elements from the dictionary, they will remain in the same order, but as soon as you add or remove an element, the new order will be completely different.

Inserting/Deleting Exception using Core Data

While this is not a fault/error its coming up as an exception. When I try to delete or insert an object using core data, the first insert or delete shows the following error:
2012-04-09 18:57:32.201 SEA[7437:fb03] * Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1046
2012-04-09 18:57:32.203 SEA[7437:fb03] 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 (5) must be equal to the number of rows contained in that section before the update (5), 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 am a bit unsure why this is coming up as the actual tableView shows the new row or deleted row. The database also shows the correct values and everything.
The code I am using is the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
}
Let me know if you need to see more code.
In the tableViewController that displays all the users. I have the following:
(void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
(void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
Here is the code for commitEditingStyle
(void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
// Get context and user to delete
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
User *userToDelete = [fetchedResultsController objectAtIndexPath:indexPath];
// Delete user from context
[context deleteObject:userToDelete];
// Save!
NSError *error = nil;
if(![context save:&error]){
// Handle the error..
}
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}

iOS application crashes when trying to insert new row in UITableView

[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:YES];
[self.tableView endUpdates];
This piece of code crashes my iOS application and returns the following error:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (0) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted).
Can someone tell me why? And how come it says that the number of rows after the update is zero, when it at the same time says 1 inserted, 0 deleted?
Update
The code above gets triggered when I receive a push notification during runtime. When that happens, the delegate adds the received data to a dictionary stored in a .plist file (which the table view uses to populate its cells with) followed by a call to a custom method in the RootViewController, where the table view is located. This method executes the code above followed by a crash. However, when I log [theDictionary count] in - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section the number increments just as it should right before crashing.
Also worth noting is that when i log self.tableview I receive a different "memory address" from the RootViewController when it loads upon launch, and when receiving a push notification. Maybe this has something to do with it?
This is my - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [theDictionary count];
}
And a picture of the error:
The numberOfRowsInSection method should return the correct number of rows whenever you make any change to the data source of table view.
For example if the total number of rows in the table is 5 and if you are going to add one more row, then the numberOfRowsInSection method should return 6.
Its easily achieved by having the data in an array or dictionary. If you want to add or delete rows just update the array or dictionary. And just return [array count] or [dictionary count] from numberOfRowsInSection method.

Deleting a cell from UITableView - error

In my iPhone app, I have a UITableView with the following method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [tableArrays count];
}
Note that tableArrays is a class variable (an NSMutableArray of NSMutableArrays - representing one NSMutableArray for each section in my tableView). Now, that UITableView supports editing, and I have the following method
- (void)tableView:(UITableView *)theTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
...some code...
NSLog(#"tableArrays count is %i",[tableArrays count]);
[tableArrays removeObjectAtIndex:indexPath.section];
NSLog(#"tableArrays is now %#",tableArrays);
NSLog(#"tableArrays count is now %i",[tableArrays count]);
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
So, I run the app and I have two sections in my table view. Row deletion works fine except when I delete the last row in a section. When I delete the last row of section 1, I see the following output, as per the code above:
tableArrays count is 2
tableArrays is now (("Blah1","Blah2"))
tableArrays count is now 1
So clearly, my tableArrays count decreases by one, but I get the following error:
...The number of sections contained in the tableView after the update (1) must be qual to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or dleted (0 inserted, 0 deleted).'
I guess in -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; you are returning the total count of objects and you want to display only one section.
Try returning one in this method.
You should also return [tableArrays count]in -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;.
It should work better like this. Also what is section in
[tableArrays removeObjectAtIndex:section]; ? Did you mean indexPath.section ?
Thank for the comment, so the method that is returning the number of section is correct, but you to do like that to delete a cell.
NSLog(#"tableArrays count is %i",[tableArrays count]);
NSMutableArray *sectionArray = [tableArrays objectAtIndex:indexPath.section];
[sectionArray removeObjectAtIndex:indexPath.row];
//[tableArrays removeObjectAtIndex:indexPath.section];
NSLog(#"tableArrays is now %#",tableArrays);
NSLog(#"tableArrays count is now %i",[tableArrays count]);
The key is to use
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
when you are trying to delete the last row in a section.

Delete UITableView row issue

I'm having a problem that I see a lot of people having... however none of the fixes are working for me. But I did notice something odd happening when I put NSLog commands in the code.
I have the standard delete method:
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.recipes removeObjectAtIndex: indexPath.row];
[tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject: indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
}
recipes is the array that holds the data source.
Theoretically this should work just fine, but I get the error:
invalid number of rows in section 1. The number of rows contained in an existing section after the update (9) must be equal to the number of rows contained in that section before the update (10), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted).
But I know where this is falling apart, when I add an NSLog into the numberOfRowsInSection, I see that the method is being called twice from the method above.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSLog(#"THIS CALLED TWICE %i",[recipes count] );
return [recipes count];
}
Anyone know what else could cause the numberOfRowsInSection method to fire twice?
Thank you for your time.
After writing this, I saw that I had the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 2;
}
Apparently, numberOfRowsInSection is called for each section... changing the code to:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
Fixed the issue.