I have some problem with deleting rows from a tableView which is filled with coreData objects.
Trying to do this:
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView beginUpdates];
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Deleting (%#)", [managedObject valueForKey:#"original_title"]);
[self.managedObjectContext deleteObject:managedObject];
[self.managedObjectContext save:nil];
[self performFetch];
[self.tableView endUpdates];
}
}
So, when I click the "Delete" button, the app crashes with the following Log:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2872.3/UITableView.m:1254
2013-08-13 18:39:17.624 RR_proto[1076:a0b] *** Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections.
The number of sections contained in the table view after the update (1) must be equal
to the number of sections contained in the table view before the update (2), plus or minus
the number of sections inserted or deleted (0 inserted, 0 deleted).'
*** First throw call stack:
I also tried to add the following line after [self.managedObjectContext save:nil]:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
But the app crashes like before.
One more thing, everytime i start the app again after a crash through delete-action, the cell which should be deleted is really gone!
Would be very nice, if somebody can help.
I know there are many questions about similar problems and I tried different things from these, without success.
Thanks!
The table view is updated automatically by the fetched results controller delegate
methods when objects are added, deleted or modified.
Therefore, to remove an object, just delete it from the managed object context.
Don't call beginUpdates, endUpdates, deleteRowsAtIndexPaths or performFetch:
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:managedObject];
[self.managedObjectContext save:nil];
}
}
Related
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
PFObject *object = [_tdlArray objectAtIndex:(_tdlArray.count - indexPath.row -1)];
[object deleteInBackground];
//found the code for removing a row.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView reloadData];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded){
[tableView reloadData];
}
}];
}
}
I am able to successfully remove the data, but my app would crash every time I tap on the delete button. I think it has something to do with the [NSArray arrayWithObject:indexPath]
These are the error message
Assertion failure in -[UITableView _endCellAnimationsWithContext:]
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. 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 (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
You want to delete the object then reload the data. Don't dispatch asynchronously the object to be deleted then tell the tableview you're deleting rows because the object has likely not been deleted yet, hence the error you're getting. Use the callback block to update the tableview after the object has been removed that way you can be sure that the object has been deleted. Also if you have a local store of the data that not bound to the data on the server, you need to remove the object from there as well.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//not sure how you're calculating the index here
PFObject *object = [_tdlArray objectAtIndex:(_tdlArray.count - indexPath.row -1)];
NSMutableArray *mutArray = [_tdlArray mutableCopy];
[mutArray removeObject:object];
_tdlArray = [NSArray arrayWithArray:mutArray];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded){
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
[tableView reloadData];
}
}];
}
}
Deleting a row from a UITableView fed by an NSFetchedResultsController causes my app to crash.
Error is:
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-2903.23/UITableView.m:1330
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '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 (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
I want only swipe-to-delete. My deletion code goes like this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView beginUpdates];
SequenceData *sd = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:sd];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
My NSFetchedResultsController is set up just like in Ray Wanderlich's tutorial (http://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller)
Number of rows is determined by:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionObjects = [[self.fetchedResultsController sections] objectAtIndex:section];
NSInteger nbObjects = [sectionObjects numberOfObjects];
return nbObjects;
}
It looks like the fetch is not updating (number of rows does not vary). Do I need to fetch myself (isn't this taking care of by the fetch controller)?
There is obviously something basic I am missing here. Don't hesitate to suggest basic answers.
I first implemented this with the controller: didChangeObject: ... method. Error was the same, but some details differed.
Edit
I believe my problem is fixed. Both answers (From CX and Martin) helped me find it. Martin got the answer because of the explanations that helped me understand a little bit better...
Don't call
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if you use a fetched results controller. Only delete the object with
[self.managedObjectContext deleteObject:sd];
The fetched results controller delegate method didChangeObject: is then called automatically,
and that calls deleteRowsAtIndexPaths:.
So in your case, the row was deleted twice, and that caused the exception.
Note that you don't need beginUpdates/endUpdates here.
If you want to delete a row you need to delete managedObject only.
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.managedObjectContext deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
// handle error
}
}
Deleting the managed object triggers the NSFetchResultController delegate methods, and they will update the tableView.
Edit
You should implement NSFetchResultController delegate method
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath{
switch(type) {
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
////
default:
break;
}
Because when you work with data source like NSFetchedResultsController, all changes must come from there and your table only reflects them.
I have a UITableViewController set up that displays my managedObjects properly when it's loaded, however when I go to delete a cell my app crashes. This is what the console has to say:
2015-05-03 09:55:20.125 MyApp[9461:663817] *** Terminating app due to
uncaught exception 'NSInternalInconsistencyException', reason: '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 (0 inserted, 1 deleted) and plus or minus the number of
rows moved into or out of that section (0 moved in, 0 moved out).'
When I reboot the app and go to my TVC, the object that I deleted the last time is deleted.
I have sections set up in my NSFetchedResultsController that is instantiated when viewDidLoad is called. I suspect I have a small issue, but I'm not sure where to go to add the missing line(s?).
I enable swipe to delete in viewDidLoad with this line:
// Enable swipe to delete
self.tableView.allowsMultipleSelectionDuringEditing = NO;
Here's my commitEditingStyle:
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete selected NSManagedObject from managedObjectContext
NSManagedObject *objectToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:objectToDelete];
[self.managedObjectContext save:nil];
// Delete the row from the data source
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
} 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
}
}
Thank you for reading. If you have any ideas, I welcome them.
The error message is saying that number of row returns from numberOfRowInSection is not matching with current number of item in table..
when you use fetchedResultsController numberOfRowsInSection should look something like this.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.fetchedResultsController fetchedObjects] count];
}
and fetchedResultsController delegate didChangeObject: is then called automatically which calls the deleteRowAtIndexPaths: inside.. so you don't need to call the deleteRowAtIndexPaths: just call deleteObject, otherwise it deletes twice.
I am creating a core data example wherein I want to delete managed objects (here called projects). Deleting the manages objects seems to work, but refreshing the tableView is a problem. Below is the log message I get when the app crashes when tableView: numberOfRowsInSection: is called.
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of rows in section 0. The number of rows contained in an
existing section after the update (13) must be equal to the number of
rows contained in that section before the update (13), plus or minus
the number of rows inserted or deleted from that section (0 inserted,
1 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).'
Here the number of rows before the deletion is 14 and after is 13. See the methods I am using for A. fetching the number of rows, which should equal the number of "projects" in core data. B. Deleting the project. C. setting the number of rows in the tableView.
A.
-(void) setupFetchResultsController {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Project"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error;
fetchedProjects = [managedObjectContext executeFetchRequest:request error:&error];
if (fetchedProjects == nil) {
// Handle the error.
}
self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
}
B.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
selectedProject = [self.fetchedResultsController objectAtIndexPath:indexPath];
[managedObjectContext deleteObject:selectedProject];
//setting up the fetch results controlleris intended to get the number rows correctly by fetching the existant projects. In the above log, the result would be 14 (not 13) without this line. Crashes with or without this line.
[self setupFetchResultsController];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
C.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int noOfRows = [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects];
return noOfRows;
}
This has nothing to do with Core Data.
You must ensure the number of rows in the table and the number of rows in the data source is always the same.
If you need to delete a row, you need to delete the row from the data source, delete it from the table, BUT -> you need to do it between those two lines of code:
[_tableView beginUpdates];
// here delete from data source, and from the table.
[_tableView endUpdates];
The problem was, I think, that I did not need to manually delete the row using:
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
I think the FetchResultsController takes care of that. Anyway I removed the line and it works.
Getting the following error when trying to delete a row from a tableview hooked to a fetched results controller:
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:1037
2012-07-12 13:11:19.921 Chef[28843:12203] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (20) must be equal to the number of rows contained in that section before the update (20), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
My code is below.
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the record from the DB
RecipeCategory *objectToBeDeleted = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.context deleteObject:objectToBeDeleted];
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
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
}
}
Using a fetch request count, and the method - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section, I have tried logging the number of DB records, and the number of rows in the table before and after the line [self.context deleteObject:objectToBeDeleted];, and they both decrease from 20 to 19.
Please help!
Edit: PS the error occurs at the deleteRowsAtIndexPath method.
It turns out that the line of code below was surplus to requirements. I guess the NSFetchedResultsController handles that as well. I also had to implement the delegate methods for NSFetchedResultsController.
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];