ios6 remove last cell in section causing error - uitableview

I am getting this error when deleting the last row in the section of a UITableView.
*** 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 (2) must be equal to the number of sections contained in the table view before the update (3), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'
My table has 3 sections IF an array has a count above 0 (users can add locations themselves). If the array is equal to 0 then the table contains only 2 section, which is where I think the problem lies, I just can't figure it out.
The code I have for deleting the cells and updating the arrays is:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* delCell = [tableView cellForRowAtIndexPath:indexPath];
delName = delCell.textLabel.text;
[self deleteLocation];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
-(void)deleteLocation {
NSLog(#"Name = %#",delName);
NSUserDefaults *customLocations = [NSUserDefaults standardUserDefaults];
NSUserDefaults *sharedPref = [NSUserDefaults standardUserDefaults];
[customLocations removeObjectForKey:delName];
[customLocations synchronize];
[customLoc removeObject:delName];
saveCustomLoc = [NSArray arrayWithArray:customLoc];
[sharedPref setObject:saveCustomLoc forKey:#"CustomLocations"];
[sharedPref synchronize];
NSLog(#"%#",saveCustomLoc);
NSLog(#"%lu",(unsigned long)[saveCustomLoc count]);
[self showAlert];
}
- (void) showAlert {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Location Deleted"
message:#"The location has been deleted"
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
}
This works great until the final cell.
An example of how my table is set up is:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
if ([customLoc count] == 0) { // If there is no data in the custom locations array the don't include that section
if (section == 0) {
return [serverSelection count];
}
else {
return [qServerSelection count];
}
}
else {
if (section == 0) {
return [customLoc count];
}
else if (section == 1) {
return [serverSelection count];
}
else {
return [qServerSelection count];
}
}
So the headings/number of rows/number of sections etc. is dependent on the contents of an Array. The fact that the section the cells are being removed from disappears (or rather suddenly has 4 cells in as section 1 moves to section 0) I think is causing the issue.
Any ideas how to work around this?

After looking into this some more I found out (with the help of some Apple forum members) that when removing the last row from a section you also need to explicitly delete the section as well.
I also added table update code at the beginning and the end as shown below:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates];
UITableViewCell* delCell = [tableView cellForRowAtIndexPath:indexPath];
delName = delCell.textLabel.text;
[self deleteLocation];
if ([customLoc count] == 0) {
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationTop];
}
else {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
[tableView endUpdates];
}
So, if my array is 0 (in effect the last row is being deleted) the section itself is removed.

Related

UITableView deleteRowsAtIndexPaths crash

I have UITableView and I made implementation for -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath :
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSUInteger beforeDeleteCount = historyArray.count;
VideoItem *video = [historyArray objectAtIndex:indexPath.row];
[[HistoryRepository sharedHistory] removeFromHistories:video];
if (self.titleSort) {
[self sortArrayByTitleAtoZWithReloadData:NO];
} else {
[self sortArrayByNormalWithReloadData:NO];
}
NSUInteger afterDeleteCount = historyArray.count;
if (beforeDeleteCount == afterDeleteCount) {
[table reloadData];
} else {
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
}
-(void)sortArrayByNormalWithReloadData:(BOOL)reload {
self.titleSort = NO;
historyArray = [[NSMutableArray alloc] initWithArray:[[HistoryRepository sharedHistory] historyArray]];
if (reload) {
[self setTableHeader];
[table reloadData];
}
}
-(void)sortArrayByTitleAtoZWithReloadData:(BOOL)reload {
self.titleSort = YES;
NSSortDescriptor * sortDescriptor = [[[NSSortDescriptor alloc]initWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)] autorelease];
historyArray = [[NSMutableArray alloc] initWithArray:[[[HistoryRepository sharedHistory] historyArray] sortedArrayUsingDescriptors:#[sortDescriptor]]];
if (reload) {
[self setTableHeader];
[table reloadData];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [historyArray count];
}
And I still get this kind of error all the time:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (27) must be equal to the number of rows contained in that section before the update (27), 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 always making a check if it's the same count so reload the table and don't delete but still i'm getting this error.
Reordering the data source array after deleting an item makes no sense if the array is already sorted.
And you must not call deleteRowsAtIndexPaths after reordering the data source array anyway.
This version of commitEditingStyle: is sufficient
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
VideoItem *video = [historyArray objectAtIndex:indexPath.row];
[[HistoryRepository sharedHistory] removeFromHistories:video];
[historyArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}

How to add/insert a new row in UITableView using user input

self.alphabets is my data source which contains all the alphabets.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{ [tableView beginUpdates];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.alphabets removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
}
if (editingStyle == UITableViewCellEditingStyleInsert) {
[self.alphabets insertObject:[self.alphabets objectAtIndex:indexPath.row] atIndex:[self.alphabets count]-1];
NSIndexPath * path1 = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
NSArray * index = [NSArray arrayWithObjects:path1, nil];
[self.tableView insertRowsAtIndexPaths:index withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
NSLog(#"%#",self.alphabets);
}
This is the code, which i am using to add or insert rows/cells in tableview by using the data already present.
But i want to add or insert a new row/cell by taking the user input. How to do it.
I have tried to use UIAlertController. Is that a right approach? But i failed. Someone please help me.
Also, i have a doubt about inserting multiple rows at a time. How to achieve it.
Here is the code which i am using to insert multiple rows at a time.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates];
if (editingStyle == UITableViewCellEditingStyleInsert) {
[self.alphabets insertObject:[self.alphabets objectAtIndex:indexPath.row] atIndex:[ self.alphabets count]-1];
NSIndexPath * path1 = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
NSIndexPath * path2 = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:0];
NSArray * index = [NSArray arrayWithObjects:path1,path2, nil];
[self.tableView insertRowsAtIndexPaths:index withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
NSLog(#"%#",self.alphabets);
}
Here is the exception i am getting.
*** 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 (11) 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 (2 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleInsert) {
UIAlertController * alert = [UIAlertController alertControllerWithTitle:#"Inserting a new row" message:#"Enter text below to add it to table view" preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
}];
UIAlertAction * ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSArray * tfArray = alert.textFields;
UITextField * tf = [tfArray objectAtIndex:0];
self.userInput = tf.text;
NSLog(#"%#",self.userInput);
[self.alphabets insertObject:self.userInput atIndex:indexPath.row];
[tableView reloadData];
}];
[alert addAction:ok];
[self presentViewController:alert animated:NO completion:nil];
}
NSLog(#"%#",self.alphabets);
}
Using the above code, I am able to add or insert new rows or cells with user input.
I used a UIAlertController to complete this task. I added a UITextfield to the alert controller and can now get input from it. It in turn adds it to the datasource and the reloaded UITableview.
self.userInput is an string used to store the user input given through a textfield in an alert.
If anyone thinks that this code can be improved, then let me know. I am always willing to improve myself.

Removing object of NSMutableArray returns exception after row deleting

Here is my problem :
I have an NSMutableArray *notes and which the source of my UITableView* _gradesTV.
I added the method - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath here :
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSLog(#"%ld", (long)indexPath.section);
NSLog(#"%#", notes);
[_gradesTV beginUpdates];
[notes removeObjectAtIndex:indexPath.section];
[_gradesTV deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
[_gradesTV endUpdates];
NSLog(#"%#", notes);
}
[_gradesTV reloadData];
}
But when i delete an element I get this error :
*** Terminating app due to uncaught exception 'NSRangeException',
reason: '*** -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
But the NSLog I make on the MutableArray show me 3 elements ...
The line removeObjectAtIndex on the notes (MutableArray) makes my app crash like it was executed twice ...
Thank you for your help ...
Github Link
if u want to pull the project and try ...
You will have to create grades with the + button then you can try to delete an item on my table view.
When adding a grade the first TextField is a string, the second one is a double value, the third one is also a double value.
Try with following code..
- (NSInteger)numberOfSections {
return [notes count];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSLog(#"%ld", (long)indexPath.section);
NSLog(#"%#", notes);
[notes removeObjectAtIndex:indexPath.section];
NSLog(#"%#", notes);
}
[_gradesTV reloadData];
}
The problem is you are deleting section instead of row.
[notes removeObjectAtIndex:indexPath.section]; should be [notes removeObjectAtIndex:indexPath.row];
Also [_gradesTV deleteSections should be [_gradesTV deleteRow...
Note: You should use [_gradesTV deleteRow... or [_gradesTV reloadData]; because both are useless. I would say just use [_gradesTV deleteRow...
Also not sure beginUpdates & endUpdates are for what. Remove the un-necessary stuff.
Firstly remove UITableView's beginUpdates and endUpdates
Secondly remove UITableView's reloadData as no need when using UITableViewCellEditingStyleDelete and UITableViewCellEditingStyleInsert
Now your code will be :
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSLog(#"Current Section : %ld", (long)indexPath.section);
NSLog(#"Before : %#", notes);
[notes removeObjectAtIndex:indexPath.section];
// Either delete some rows within a section (leaving at least one) or the entire section.
id sectionDataSource = [notes objectAtIndex:indexPath.section];
if ([sectionDataSource count] > 0)
{
// Section is not yet empty, so delete only the current row.
[_gradesTV deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
else
{
// Section is now completely empty, so delete the entire section.
[_gradesTV deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
}
NSLog(#"After : %#", notes);
}
}
Ok I figured from where came my mistake, it was just that I didn't used the same MutableArray to get the number of sections ...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [notes count];
}
The old method had an old NSMutableArray I was using to test my code ...
Thank you all even if I was just too noob to notice it by myself ....

Why am I getting an assert Error When trying to Delete a section in a tableview ios

I have a tableView with some sections, which all have a footer, and then I have a tableViewFooter on the Tableview itself.
If I scroll down to the bottom of my tableview and delete the last item(therefore deleting the section altogether) in any sections above the last section (second last and up) it gives me this error
2014-02-21 13:19:55.066 xxxx[5436:60b] *** Assertion failure in -[UIViewAnimation initWithView:indexPath:endRect:endAlpha:startFraction:endFraction:curve:animateFromCurrentPosition:shouldDeleteAfterAnimation:editing:], /SourceCache/UIKit/UIKit-2903.23/UITableViewSupport.m:2661
Uncaught exception: Cell animation stop fraction must be greater than start fraction
at endUpdates
this is my code
[self.tableView beginUpdates];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
if(indexPath != nil){
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
NSMutableDictionary *dic =[sec.items objectAtIndex:indexPath.row];
Product* product = [dic valueForKey:PRODUCT];
//removing the item in the section
[sec.items removeObject:dic];
//deleting item from products
NSMutableArray *temp = [NSMutableArray array];
for (Product *p in self.dataCon.listPersister.products) {
if ([p.product.objId isEqualToString: product.product.objId]) {
[temp addObject:p];
}
}
for (Product *p in temp) {
[self.dataCon.listPersister.products removeObject:p];
}
//if it was the last object in section, delete the section else just delete the single row
if(sec.items.count == 0)
{
[self.sections removeObject:sec];
[self.footers removeObjectAtIndex:indexPath.section];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} else
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
USFooterView *footer = [self.footers objectAtIndex:indexPath.section];
footer.totalLabel.text = [self.dataCon.listPersister getTotalForShopId:sec.title];
self.footerView.totalLabel.text = [self.dataCon.listPersister getTotalForAllProducts];
}
}
[self.tableView endUpdates];
I had the same code earlier, just without my tableView and table sections having footers, where it worked, so I think they might be the problem, but I'm not entirely sure that's the reason it's acting up.
I have seen this post
UITableView tableFooterView may cause crash?
And the post that it links to, but that didn't help me.
Any help is appreciated :)
In the else statement you delete row from table view:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
But not from data source. Delete row from array which you use as data source and it should works.
I found a "fix", but I'm avoiding the use of sectionFooter, because that seems to be bugged.
I created an extra cell at the end of each section, with the same setup I had for my footer View before, and made that last cell not editable with
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
if (sec.items.count != indexPath.row) {
return YES;
} else
return NO;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [sec.items count] +1 ;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"normalcell";
static NSString *CellIdentifier1 = #"footercell";
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
if (indexPath.row != sec.items.count) {
//use normal type of cell
return cell;
} else{
//use footer type of cell
return cell;
}
}
So the last cell Imitates a "footer", but it's not stuck to the bottom of the frame, but I'll have to live with that. It's better than crashes.
Try using UITableViewRowAnimationLeft or UITableViewRowAnimationRight as the delete row animation(deleteRowsAtIndexPaths:withRowAnimation:).
It crashed for me when using UITableViewRowAnimationAutomatic, but not with the other two. I have not tried all of them but it seems to be a bug with the animation code for some of the options.

How to delete a row from UITableView? [duplicate]

This question already has answers here:
How to delete a row from UITableView
(5 answers)
Closed 9 years ago.
I want to delete a row from a tableView by using alertView message to confirm, but got some error output:
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).'
Setup some of private member in SomeViewController interface
#interface SomeViewController ()
{
NSMutableArray *listItems;
NSString *itemId;
NSIndexPath *rowIndexPath;
}
In SomeViewController.m
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [listItems count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[listItems objectAtIndex:section] count];
}
#pragma mark - Table view operation
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//add code here for when you hit delete
UIAlertView *deleteAlert = [[UIAlertView alloc] initWithTitle:#"Message"
message:#"Are you sure?"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:#"Cancel", nil];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
itemId = cell.textLabel.text;
rowIndexPath = indexPath;
deleteAlert.tag = 1;
[deleteAlert show];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 1)
{
if (buttonIndex == 0) {
NSLog(#"itemId: %#", itemId);
NSLog(#"rowIndexPath: %#", rowIndexPath);
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:rowIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self.tableView reloadData];
}
}
}
I can correctly set the tableView data and show deletion option, but can't figure it out why that error occurred.
Delete the datasource before you delete cell.
Hope it helps
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 1)
{
if (buttonIndex == 0) {
NSLog(#"itemId: %#", itemId);
NSLog(#"rowIndexPath: %#", rowIndexPath);
[self.tableView beginUpdates];
[[listItems objectAtIndex:section] removeObjectAtIndex:rowIndexPath.row]
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:rowIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[self.tableView reloadData];
}
}
Remember every time you do a delete action, you should always modify your data source first.
You can simply remove the object from the array, then reload the UITableView. Like So:
[YourArray removeObjectAtIndex:1]; //Change the index to which cell you want to "delete"
[YourTableView reloadData];
Or do as stated here: How to delete a row from UITableView.

Resources