I have a custom UITableView I'm trying to add a row to the tableView. Here is my code:
- (IBAction)addRow:(id)sender
{
NSInteger indexPath = [self.rowArray count] - 1;
// I'm adding an object to an array of all the cell ids I have.
[self.rowArray insertObject:#"rowSix" atIndex:indexPath]; // rowSix is a cell ID
[self.myTableView beginUpdates];
[self.myTableView endUpdates];
}
This is the error that it outputs:
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 (8) must be equal to the number of rows contained in that section before the update (7), 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).'
Update
In cellForRowAtIndexPath:
NSString *cellID = [self.rowArray objectAtIndex:[indexPath row]];
customCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
Try this one-
//Update data source with the object that you need to add
[tableDataSource addObject:newObject];
NSInteger row = //specify a row where you need to add new row
NSInteger section = //specify the section where the new row to be added,
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
try this -
- (IBAction)addRow:(id)sender
{
[self.rowArray addObject:#"New Item"];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:[self.rowArray count] inSection:0];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
Related
- (void)registerCellCallBack:(id<CellCallBack>)callBack {
int i = 0;
for(; i < _dataSource.count; ++i) {
id<CellCallBack> oldCallBack = _dataSource[i];
if([callBack key] <= [oldCallBack key]) {
break;
}
}
if(i < _dataSource.count) {
[_dataSource insertObject:callBack atIndex:i];
} else {
[_dataSource addObject:callBack];
}
[self.table beginUpdates];
[self.table insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.table endUpdates]; //--crashed here called "NSInternalInconsistencyException(SIGABRT)"
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _dataSource.count;
}
The code which even was well in iOS9, crashed in iOS10, but not appear when I debug my code.
Crash info:
Invalid update: invalid number of rows in section 0. The
number of rows contained in an existing section after the update (7)
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) and plus or minus the number
of rows moved into or out of that section (0 moved in, 0 moved out).
You need to change as per below:
To
[self.table reloadData];
From
[self.table beginUpdates];
[self.table insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.table endUpdates]; //--crashed here called "NSInternalInconsistencyException(SIGABRT)"
I am doing very usual stuff.
First modifying the data model and then updating the TableView. But sometimes app crashes.
And throws error something like that (depends on insertion or deletion).
Fatal Exception: NSInternalInconsistencyException
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (10) 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, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
Here is my insertion code.
[self.tableView beginUpdates];
[self.stories insertObject:story atIndex:0];
NSArray *indexPaths = #[[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
and Deletion code.
[tableview beginUpdates];
[stories removeObject:story];
NSIndexPath *indexpath = [tableview indexPathForCell:cell];
if(indexpath) {
[tableview deleteRowsAtIndexPaths:#[indexpath] withRowAnimation:UITableViewRowAnimationTop];
}
[tableview endUpdates];
Insertion Code:
[self.stories insertObject:story atIndex:0];
[self.tableView beginUpdates];
NSArray *indexPaths = #[[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
Deletion Code:
[stories removeObject:story];
[tableview beginUpdates];
NSIndexPath *indexpath = [tableview indexPathForCell:cell];
[tableview deleteRowsAtIndexPaths:#[indexpath] withRowAnimation:UITableViewRowAnimationTop];
[tableview endUpdates];
Note: You should not add/remove object to an array inside updating your tableview's rows.
Either do [tableview reloadData]; or [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
Also you are doing database operation in-between [self.tableView beginUpdates];
and [self.tableView endUpdates];. It's not proper.
As per the error suggests, there is a mismatch in table view rows and data source of that table view.
I have a swift table view/collection view. Is there anyway I could append items to the end of the container ? Like an upside down stack or something ?
You need insert rows at in UITableView and collection view for that first need to insert in you data source.
For TableView
//Update data source with the object that you need to add
[tableDataSource addObject:newObject];
NSInteger row = [tableDataSource count]-1 ;//specify a row where you need to add new row in your case last
NSInteger section = //specify the section where the new row to be added,
//section = 0 here since you need to add row at first section
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
For CollectionView
[self.myCollectionView performBatchUpdates:^{
[collectionDataSource addObject:newObject];
NSInteger row = [collectionDataSource count]-1 ;//specify a row where you need to add new row in your case last
to add new row in your case last
NSInteger section = //specify the section where the new row to be added,
//section = 0 here since you need to add row at first section
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.myCollectionView insertItemsAtIndexPath: indexPath];
} completion:nil];
I have a UITableView with expandable sections. When a user goes to another view, I need all the expanded sections to collapse, which I'll need to put in the viewWillDisappear method.
I've found solutions only on how to delete all rows from a table view at once, but is there a way to delete all the rows from a specific section?
EDIT:
I have figured out a solution, but I'm not sure if it's optimal or can lead to inefficiencies in the future. Whenever a cell is expanded, it gets added to an NSMutableIndexSet. So in my viewWillDisappear method, I iterate over the expanded sections like so:
-(void)viewWillDisappear:(BOOL)animated
{
if (expandedSections.count != 0) {
NSLog(#"COLLAPSING CALLED");
[self.tableView beginUpdates];
NSUInteger section = [expandedSections firstIndex];
do
{
NSInteger rows;
NSMutableArray *tmpArray = [NSMutableArray array];
rows = [self tableView:self.tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
for (int i=1; i<rows; i++) {
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i inSection:section];
[tmpArray addObject:tmpIndexPath];
}
[self.tableView deleteRowsAtIndexPaths:tmpArray withRowAnimation:UITableViewRowAnimationTop];
NSIndexPath *expandableCellIndexPath = [NSIndexPath indexPathForRow:0 inSection:section];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:expandableCellIndexPath];
cell.accessoryView = [DTCustomColoredAccessory accessoryWithColor:[self.colorHolder objectAtIndex:section] type:DTCustomColoredAccessoryTypeRight];
section = [expandedSections indexGreaterThanIndex:section];
} while (section != NSNotFound);
[self.tableView endUpdates];
}
}
Please let me know if this is a good solution or, if I'm suspecting correctly, if this will lead to slower transitions between views in the future when there are more rows in each expanded section. Any help or advice would be appreciated. Thanks.
If you want to animate changes, you will need to first update your data source (to return 0 for number of rows in the section) then remove section and add section at the same index path in one transaction between [tv beginUpdates] [tv endUpdates]
Otherwise just update the data source and reload the table on your way back to the VC (if you don't want any animations)
I did not read this in detail but surely the for loop should start at zero.
for (int i=0; i<rows; i++) {
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i inSection:section];
[tmpArray addObject:tmpIndexPath];
}
otherwise you will only delete all but the first cells in the section.
I am having problems with deleting rows from table view. I am using the code below when the delete button in the row was pressed:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:control.tag-100 inSection:0];
[resultList removeObjectAtIndex:indexPath.row];
[resultView beginUpdates];
[resultView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[resultView endUpdates];
//[resultView reloadData];
First row was deleted successfully but then, indexes were not correct. So when I delete the last row, it gives index out of bounds exception.
The cell generation code is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"personalizeTableCell";
PersonalizeCell *cell = (PersonalizeCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[PersonalizeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.title.text = #"text";
cell.rateView.tag = indexPath.row + 100;
return cell;
}
Where am I wrong?
UPDATE:
for (NSInteger j = 0; j < [venuesTableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [venuesTableView numberOfRowsInSection:j]; ++i)
{
PersonalizeCell* cell = (PersonalizeCell*)[venuesTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]];
cell.rateView.tag = 100 + i;
}
}
solved my problem. Thanks to Nenad M.
Assuming your [NSIndexPath indexPathForRow:control.tag-100 inSection:0]; returns the right index path....
Did you try moving the removeObjectAtIndex call inside the begin-/end-updates "bracket"?:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:control.tag-100 inSection:0];
[resultView beginUpdates];
[resultList removeObjectAtIndex:indexPath.row];
[resultView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[resultView endUpdates];
UPDATE:
Obviously your cell.rateView.tag become wrong, after youe delete your first cell.
So after every deletion (i.e. every removeObjectAtIndex...) you must re-iterate over your remaining tableview-cells an re-assign the proper tag-value (cell.rateView.tag = indexPath.row + 100)! Otherwise your [NSIndexPath indexPathForRow:control.tag-100 inSection:0]; will return a wrong indexPath, therefore leading to your out of bounds error!
Re-assigning the tag-values:
You don't have to reload the entire table, just loop through the remaining cells an re-assign the tag-value after [resultView endUpdates];:
NSMutableArray *cells = [[NSMutableArray alloc] init];
for (NSInteger j = 0; j < [tableView numberOfSections]; ++j)
{
for (NSInteger i = 0; i < [tableView numberOfRowsInSection:j]; ++i)
{
[cells addObject:[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:j]]];
}
}
Now do:
for (PersonalizeCell* cell in cells)
{
cell.rateView.tag = // new calculated tag
}
(Or do the re-assignment even in the inner-loop of the first code-snippet directly.)
Here's some really typical code for the whole process, two lines from the table in the example:
Note that facebookRowsExpanded is a class variable you must have:
if ( [theCommand isEqualToString:#"fbexpander"] )
{
NSLog(#"expander button......");
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSArray *deleteIndexPaths;
NSArray *insertIndexPaths;
facebookRowsExpanded = !facebookRowsExpanded;
// you must do that BEFORE, not AFTER the animation:
if ( !facebookRowsExpanded ) // ie, it was just true, is now false
{
deleteIndexPaths = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:2 inSection:0],
[NSIndexPath indexPathForRow:3 inSection:0],
nil];
[tableView beginUpdates];
[tableView
deleteRowsAtIndexPaths:deleteIndexPaths
withRowAnimation: UITableViewRowAnimationMiddle];
[tableView endUpdates];
}
else
{
insertIndexPaths = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:2 inSection:0],
[NSIndexPath indexPathForRow:3 inSection:0],
nil];
[tableView beginUpdates];
[tableView
insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation: UITableViewRowAnimationMiddle];
[tableView endUpdates];
}
// DO NOT do this at the end: [_superTableView reloadData];
return;
}
NOTE: your code for numberOfRowsInSection must use facebookRowsExpanded
(it will be something like "if facebookRowsExpanded return 7, else return 5")
NOTE: your code for cellForRowAtIndexPath must use facebookRowsExpanded.
(it has to return the correct row, depending on whether or not you are expanded.)