NSInternalInconsistencyException error removing tableview cell - ios

I'm getting an NSInternalInconsistencyException when trying to remove a single row from my table view. The table view contains several sections, with one row per section. I'm doing the following:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:index];
[indexPathToDelete addObject:indexPath];
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:indexPathToDelete withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
Before the update, I've updated my model (a NSArray containing one object for every row of my table view), deleting the object at index.
What am I doing wrong?
I've checked that I'm returning the correct number of sections after deleteing the object from the model and the table in the method -(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView.
Any hints?

Since you have one section per item, you also need to tell the table view that the section itself has been deleted with a call to deleteSections:withRowAnimation: inside your beginUpdates/endUpdates block.

Related

How to get last cell index path in iOS?

I am building an IOS application where I am using tableView. Now when I reached to last cell I load +10 data from localDB.
After fetching the data I reloaded the tableView that I don't need in place of reload I want to used updated tableView. for that SOF suggested me below code.
[[self tableView] beginUpdates];
[[self tableView] reloadRowsAtIndexPaths:____ withRowAnimation:UITableViewRowAnimationAutomatic];
[[self tableView] endUpdates];
I don't what should be there in reloadRowsAtIndexPaths value. Please can some help me in understanding this above line of code.
You can get the indexPath of the last row in last section like this.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(numberOfRowsInLastSection - 1) inSection:(numberOfSections - 1)];
Here, numberOfSections is the value you return from numberOfSectionsInTableView: method. And, numberOfRowsInLastSection is the value you return from numberOfRowsInSection: method for the last section in the table view.
You don't need to reload cells, because there are no cells after your 10th cell. You want your users to see more cells when they get to the last one as in a news app. The solution is to inset more rows below, here is how:
1- create the rows you want to insert
NSMutableArray *newRows = #[[NSIndexPath indexPathForRow:10 inSection:0]];
3- update your data source array
2- [self.tableView insertRowsAtIndexPaths: newRows, UITableViewRowAnimationAutomatic];

Objective-C - iOS UITableView bottom row crash on delete

I am extending the learn iOS programming today tutorial to include delete functionality.
I have modified the tableView didSelectRowAtIndexPath: method thusly:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
ToDoItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
if (tappedItem.completed) {
[tableView beginUpdates];
[self.toDoItems removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom];
[tableView endUpdates];
} else {
tappedItem.completed = YES;
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
In a section with three rows, it works as expected. Tapping produces a checkmark, tapping a check marked item deletes it. But if I tap the bottom row, it crashes with 'attempt to delete row 2 from section 0 which only contains 2 rows before the update'. Note this is happening when the other two rows are still there (my searches found numerous posts where there was a crash when someone was deleting the last remaining row--not the case here). The bottom row will mark itself completed just fine.
Also note, moving the array changing call out of the beginUpdates block changes the error from row 2 to row 3 ... contains 3 rows.
TIA for any assistance.
Edit:
I have fixed the problem by moving [tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone]; inside of the else block. Can someone explain why?
If you use deleteRowsAtIndexPaths, there is no point in trying to reload the row that you deleted. And, obviously, if you try to reload a cell for an indexPath that is no longer valid, then you will have the sort of problem you describe.
Let's say you have 10 rows, you don't want to say, effectively, "delete the tenth row; now reload the tenth row in a table that now only has nine rows." You can easily imagine why that is problematic.
In this case, you should remove the call to reloadRowsAtIndexPaths altogether. You only have to call reload... if the contents of some of the remaining cells change. If you're just inserting or deleting rows, then just do that, and no call to reloadRowsAtIndexPaths is needed.

Assertion failure in -[UITableView _endCellAnimationsWithContext:

I'm an amateur at best, and stuck on this error! surely something simple...
- (void)addTapped:(id)sender {
TechToolboxDoc *newDoc = [[TechToolboxDoc alloc] initWithTitle:#"New Item" thumbImage:nil fullImage:nil];
[_itemArray addObject:newDoc];
//[self.tableView beginUpdates];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_itemArray.count-1 inSection:0];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
NSLog(#"%#",indexPath);
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:YES];
[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
[self performSegueWithIdentifier:#"MySegue" sender:self];
//[self.tableView endUpdates];
it is breaking on the line the says
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:YES];
You need to add [UITableView beginUpdates] and [UITableView endUpdates] around:
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:YES];
From the class reference:
Note the behavior of this method when it is called in an animation
block defined by the beginUpdates and endUpdates methods. UITableView
defers any insertions of rows or sections until after it has handled
the deletions of rows or sections. This happens regardless of ordering
of the insertion and deletion method calls. This is unlike inserting
or removing an item in a mutable array, where the operation can affect
the array index used for the successive insertion or removal
operation. For more on this subject, see Batch Insertion, Deletion,
and Reloading of Rows and Sections in Table View Programming Guide for
iOS.
I think you are inserting the row in your tableView but not updating the number of rows at section that's why you are getting error in this line. So along with inserting the row You should also increase the array count or whatever you are using to show the number of rows in table view.
In my case, I was using Parse and I deleted a few users (one of which was my iPhone simulator). I got this error whenever refreshing the tableView.
I just deleted the app from the Simulator and made a new account and it works flawlessly.
#droppy's answer helped give me the lightbulb moment of what was wrong!
Hope that helps someone.

Row deletion does not refresh table view in ios app

I have spent hours searching for the solution with out any luck. I am trying to delete a row (also deselect same row) programmatically. After row deletion call below, UITableViewDelgate methods get called expectedly and data source is updated but UITableView is not refreshed. deselectRowAtIndexPath call also does not work. I tried all kinds of scenarios as shown by commented lines.
Here is my code:
checkoutPerson is called as a result of observer listening for NSNotificationCenter messages.
- (void) checkoutPerson: (NSNumber*) personId {
Person *person = [_people objectForKey:personId];
if( person )
{
// Remove person from data source
int rowIndex = person.rowIndex;
S2Log(#"Deleting row number=%d", rowIndex);
[_allKeys removeObjectAtIndex:rowIndex];
[_people removeObjectForKey: personId];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
//[[self tableView] beginUpdates];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
S2Log(#"Deleting indexPath row=%d", [indexPath row]);
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
//[[self tableView] endUpdates];
S2Log(#"Reloading data");
//[[self tableView] reloadData];
//[self performSelector:#selector(refreshView) withObject:nil afterDelay:1.5];
//[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}
}
I will appreciate for help.
Thanks
-Virendra
I believe deleted cell is not being recycled. If I delete row in the middle, last row is always erased (since there is one less item) but the deleted row remains.
Use the above code between two function for table view
[tableView beginUpdates];
// the deletion code from data source and UITableView
[tableView endUpdates];
By calling this functions you are telling UITableView that you are about to make updates for deleting your cell.
Edit
The other problem I see with your code is you first delete the data from the data source.
Now you are asking for the UITableViewCell (which actually reloads the UITableView)
and then you are deleting the row from UITableView
I guess you should fetch the UITableViewCell before deleting values from your data source.
I found the problem. It has nothing to do with the code I posted above. It is syncing problem between visual display and the contents of data source. I have an embedded UITableView as part of a composite view. In composite view's controller, I was wiring up UITableView's delegate and data source to an instance of UITableViewController. Instead of this, I should have set UITableViewController's tableView property to the embedded UITableView. It seems that UITableView has to be contained within UITableViewController in order to correctly sync up table view visual display to the contents of data source. This also fixes row deselection and scrolling. I also needed to delay reloadData call in which case deleteRowsAtIndexPaths:withRowAnimation is not required. All you need is to modify the contents of your data source and call reloadData with a delay of 1.5 Seconds.
Thanks to all for great help.

cellForRowAtIndexPath returns nil for any section other than the first

I have a UITableViewController and its UITableView is static with three sections. I am trying to populate my static sections in viewWillAppear like so:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] detailTextLabel] setText:#"Stuff"];
[[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] detailTextLabel] setText:#"Other stuff"];
...
}
The first line populates row 0 in section 0 fine but the second line (and in fact any cellForRowAtIndexPath call to any section other than the first) returns a nil cell.
I did a few tests to try and narrow down the problem:
[self.tableView numberOfSections] // returns 3 - which is correct
[self.tableView numberOfRowsInSection:1] // return 4 - which is unique and correct for my second section
so, the table view has the correct sections it seems but:
[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] // nil
I am very confused - does anyone have any idea what might be causing it?
I have nothing special in the code: it's a straight-forward static table created in the storyboard with all three sections set up from the start; the view controller is correctly a subclassing UITableViewController; I can populate section 0 perfectly and all works as I would expect.
Thanks for any help.
If the cells aren't on screen when the view controller initially appears, they have probably not been set up. You should create outlets to the cells you are interested in instead and modify the values that way.

Resources