Add row to UITableView crash if initially empty table - uitableview

I'm using the code below to refresh a tableview which I just added a row into. The code works for adding a row to the table if there is at least 1 row already in the table. However, it crashes if it was initially an empty table.
NSArray *indexPaths = [NSArray arrayWithObjects: [NSIndexPath indexPathForRow:[commentsArray count] inSection:0], nil];
[commentsTableView beginUpdates];
[commentsArray addObject:newestEntry];
[commentsTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationBottom];
[commentsTableView endUpdates];
[commentsTableView scrollToBottom:YES];
The crash response I get is:
*** 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 (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 help me?

Well, the error is telling you the when UIKit calls tableView:numberOfRowsInSection is returning 0 both before and after your updates. So either you have an error in that method or in your update. You said in comment to a previous answer that your tableView:numberOfRowsInSection is correct, then it must be the update. If commentsArray is nil in the first update for some reason that might explain things. Perhaps try the following:
NSAssert(commentsArray, #"commentsArray is nil");
[commentsArray addObject:newestEntry];
The assert will be ignored in a release build, so use a debug build.

Just make sure the array which you are using to update is the same as Array which you are using while reloading the Table.
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [commentsArray count];
}
If this couldn't solve your problem, please put some code over here.

Related

UITableView using deleteRowsAtIndexPaths but crashing app if new row created (vie a message being received)

in my app I have a UITableView containing messages. Each row is one message. When I delete a message, I first delete it from the messages array and then use deleteRowsAtIndexPaths:
int index = (int)[self.messages indexOfObject:message];
[self.messages removeObject:message];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
NSArray *indexes = [[NSArray alloc] initWithObjects:indexPath, nil];
[self.tableView deleteRowsAtIndexPaths:indexes withRowAnimation:UITableViewRowAnimationLeft];
This works fine however when a new message is received, the push notification triggers the table refresh. Sometimes a new message will be placed in the array just before deleteRowsAtIndexPaths finishes executing and hence the app crashes because the number of rows in the table after the method completes does not equal the number of rows before minus the number of deleted rows. Example error message:
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).'
Has anyone come across a problem like this before? Is there another approach or workaround I can take to stop the app crashing like this?
Any pointers would be great
I had the same kind of issue. And what i did:
declare activity flag for you update:
#property (nonatomic, assign) BOOL canEdit;
insert self.canEdit = NO when your perform some update operation
override willDisplayCell and check, if there will display last cell of your tableview's datasource array (so, update will end after that cell)
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([dataArray count]- 1 == indexPath.row && self.canEdit == NO)
self.canEdit = YES;}
When you receive push notification - check this flag, and if it's YES - perform update. If no - request update after some time

iOS Invalid Row Update Exception

I'm making an SMS-style component of an iOS app. The messaging view is a UITableView. I want to add a new row to the table for both a sent message and an incoming message. The call to add a row for an incoming message comes through KVO via a dedicated serial thread. Similarly through KVO for send success, the call to add a row for a sent message comes from a different dedicated serial thread. If there is a call to add a row from an incoming message and another call to add a row for a sent message at near the exact same time, the app throws an exception for invalid rows:
'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (31) must be equal to the number of rows contained in that section before the update (29), 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).'
Is this a thread safety issue? How can I fix it? Here is the relevant method called from the two different threads:
-(void)addRowToMessageTable {
dispatch_async(dispatch_get_main_queue(), ^{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: [_messageTable numberOfRowsInSection:0] inSection: 0];
[_messageTable beginUpdates];
[_messageTable insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[_messageTable endUpdates];
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _privateMessages.count;
}
Table view update method internally loads the table again so it again calls the delegate method . You are getting the exception because you have added the row to the table but you have not added that object to the _privateMessages array . Add same object to array so the count will be same before and after update .

tableView editing delegate method not working(Invalid update: invalid number of rows in section 0)

'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (6) must be equal to the number of rows contained in that section before the update (6), 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).
->#property (nonatomic ,strong) NSMutableArray *name;
- (id)initWithFrame:(CGRect)frame viewName:(NSInteger)viewDecide datasourceName:(NSMutableArray *)nameRef
{
}
when i intialize the above MutableArray in this class
like
self.name=[NSMutableArray alloc]initWithObjects:#"1",#"2",#"3", nil];
then using table view editing delegate method , every thing works fine , when i click the button the table goes in edited mode with deletion and insertion.
but when i initialize the same array with data source which i passed in above method . the data inside the tableview render proprely but when i click the edit button its crash.
note:- i used
for row count-
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return[self.name count];
}
and for inserting
[strongSelf.tableViewDataView1 insertRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:0], nil] withRowAnimation:UITableViewRowAnimationLeft];
and for deleting i used
[strongSelf.tableViewDataView1 deleteRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:0], nil] withRowAnimation:UITableViewRowAnimationLeft];
:-strongSelf is used because this happening inside a block.
Your are removing the item from the table , not from your array. You have to remove the same from the array so that the count will remain same.

deleteRowsAtIndexPaths:withRowAnimation: not working

I'm trying to delete nicely the cells of mu UITableView :
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self deleteModelForIndexPath:indexPath];
[mainTableView beginUpdates];
[mainTableView deleteRowsAtIndexPaths:indexPath withRowAnimation:UITableViewRowAnimationTop];
[mainTableView endUpdates];
}
}
This throws the following exception :
*** 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).'
I don't get the (0 inserted, 0 deleted) part. Why wouldn't it work ?
Thanks
You should update the table data at the same time. If you are using an array object for each row, remove the deleted one from the array.
The error means what it says:
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).'
This is probably one of the better error messages out there and it basically means that the amount data backing your table view is out of sync with it's state in the UI, specifically the number of sections (hint: check –numberOfSectionsInTableView:) before/after your delete. If you delete a row/section from the table, you need to make sure you delete that row/section from the data backing the table.
(0 inserted, 0 deleted)
Means that the API didn't find that your data source had any deleted sections.
Are you absolutely certain that is doing what you think it is doing:
[self deleteModelForIndexPath:indexPath];
You shouldn't use beginUpdates and endUpdates methods for such an operation. You should simply do:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self deleteModelForIndexPath:indexPath];
[mainTableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationTop];
}
}
Also you can take a look at the documentation about beginUpdates:
Call this method if you want subsequent insertions, deletion, and
selection operations (for example, cellForRowAtIndexPath: and
indexPathsForVisibleRows) to be animated simultaneously. This group of
methods must conclude with an invocation of endUpdates. These method
pairs can be nested. If you do not make the insertion, deletion, and
selection calls inside this block, table attributes such as row count
might become invalid. You SHOULD NOT call reloadData within the group;
if you call this method within the group, you will need to perform any
animations yourself.

Can't delete row in UITableView

Hi the following function doesn´t work for me and i don´t get why....
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the row from the data source
NSString *selectedCategory = [self.daten objectAtIndex:indexPath.row];
[self.daten removeObjectAtIndex:indexPath.row]; //we assume, that mySourceArray is a NSMutableArray we use as our data source
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
// ...
}
}
I don´t have any Sections...I always get that exception:
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 (19) must be equal to the number of rows contained in that section before the update (19), 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).'
* First throw call stack:
(0x342692a3 0x33a3b97f 0x3426915d 0x3922e2af 0x36dd5b83 0x5453d 0x36f6b5d9 0x36e8c0ad 0x36e8c05f 0x36e8c03d 0x36e8b8f3 0x36e8c0ad 0x36e8c05f 0x36e8c03d 0x36e8b8f3 0x36e8bde9 0x36db45f9 0x36da1809 0x36da1123 0x34f195a3 0x34f191d3 0x3423e173 0x3423e117 0x3423cf99 0x341afebd 0x341afd49 0x34f182eb 0x36df5301 0x24343 0x38e34b20)
libc++abi.dylib: terminate called throwing an exception
You need to update your number of rows in numberOfRowsInSection: method. As i can see, you need to return your [self.daten count] in this method.
As its told, the system checks this number before/after update/delete to keep data integrity.
You always have a section :)
Implement it like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.daten count];
}
Also, remember that you need to set both UITableViewDelegate and UITableViewDataSource to self.
OK, the problem is: NSMutableArray's removeObjectAtIndex: method does not resize it. So you have to handle it by yourself. Create new array with right capacity and move the rest of elements to it. You can google how to resize NSMutableArray.

Resources