iOS application crashes when trying to insert new row in UITableView - iphone

[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:YES];
[self.tableView endUpdates];
This piece of code crashes my iOS application and returns the following error:
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 tell me why? And how come it says that the number of rows after the update is zero, when it at the same time says 1 inserted, 0 deleted?
Update
The code above gets triggered when I receive a push notification during runtime. When that happens, the delegate adds the received data to a dictionary stored in a .plist file (which the table view uses to populate its cells with) followed by a call to a custom method in the RootViewController, where the table view is located. This method executes the code above followed by a crash. However, when I log [theDictionary count] in - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section the number increments just as it should right before crashing.
Also worth noting is that when i log self.tableview I receive a different "memory address" from the RootViewController when it loads upon launch, and when receiving a push notification. Maybe this has something to do with it?
This is my - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [theDictionary count];
}
And a picture of the error:

The numberOfRowsInSection method should return the correct number of rows whenever you make any change to the data source of table view.
For example if the total number of rows in the table is 5 and if you are going to add one more row, then the numberOfRowsInSection method should return 6.
Its easily achieved by having the data in an array or dictionary. If you want to add or delete rows just update the array or dictionary. And just return [array count] or [dictionary count] from numberOfRowsInSection method.

Related

ios 5: Invalid update: invalid number of rows in section

I only have this problems with iOS 5, no problems with iOS 6
This is my log
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 3. 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 (1), 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).'
And my code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[dictionary allKeys] count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSArray *keyArray = [dictionary allKeys];
return [[dictionary objectForKey:[keyArray objectAtIndex:section]] count];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
= if (editingStyle == UITableViewCellEditingStyleDelete)
{
//First get all the keys of dictionary into one array
NSArray *sectionsArray = [dictionary allKeys];
//Get all the data of tapped section into one array by using indexpath.section
NSMutableArray *objectsAtSection = [dictionary objectForKey:[sectionsArray objectAtIndex:indexPath.section]];
//remove the particular object by using indexPath.row from the array
[objectsAtSection removeObjectAtIndex:indexPath.row];
// Update dictionary
[table beginUpdates];
// Either delete some rows within a section (leaving at least one) or the entire section.
if ([objectsAtSection count] > 0)
{
[dictionary setObject:objectsAtSection forKey:[sectionsArray objectAtIndex:indexPath.section]];
// Section is not yet empty, so delete only the current row.
// Delete row using the cool literal version of [NSArray arrayWithObject:indexPath]
[table deleteRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}else{
[dictionary removeObjectForKey:[sectionsArray objectAtIndex:indexPath.section]];
// Section is now completely empty, so delete the entire section.
[table deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
}
[table endUpdates];
}
}
in iOS 5, after delete some row, and some section, i have this problems.
Could you please help me?
Need to add
[self.tableView reloadData];
and
self.editing = YES;
This is needed because the table view doesn't initially have information about its data source and delegate; if you create a table view, it always needs to be sent a reloadData message as part of its initialization.
Hope it will Help you
Ok, it seems I found an answer. See this SO question and answer, explaining why using allKeys method is bad.
As long as you don't add or remove any elements from the dictionary, they will remain in the same order, but as soon as you add or remove an element, the new order will be completely different.

TableView app terminated due to 'NSInternalInconsistencyException'

I'm trying to get the hang of UITableViews and everything that goes with it. At the moment I have the following code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell.textLabel setText:[NSString stringWithFormat:#"I am cell %d", indexPath.row]];
return cell;
}
- (IBAction)killItem {
NSIndexPath *indexToDelete = [NSIndexPath indexPathForRow:2 inSection:0];
[tbl deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexToDelete] withRowAnimation:UITableViewRowAnimationRight];
}
And I get the following error when initiating the "killItem" function:
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 (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).'
The way I understand it tableViews basically have a delegate and a data source where the data source, among other things, determines how many rows should be in the tableView. Through some searches here at stackoverflow I've found that this error is caused when the "data source doesn't match reality", when it's searching for rows that don't exist, that I have deleted.
I might have gotten this wrong but that's what I think is doing it. So my question is, how do I get these to match so that I can avoid this error?
For reference, I've looked in to the following posts without understanding what I need to do:
Error : Number of Rows In Section in UITableView in iPhone SDK
Slide UITableViewCell
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSMutableArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
Adding [tbl reloadData], [tbl beginUpdate] ... [tbl endUpdate] in the killItem function, doesen't seem to help my problem eighter.
Thank you in advance,
Tobias Tovedal
Tobias, what you need to do when deleting rows is
// tell the table view you're going to make an update
[tableView beginUpdates];
// update the data object that is supplying data for this table
// ( the object used by tableView:numberOfRowsInSection: )
[dataArray removeObjectAtIndex:indexPath.row];
// tell the table view to delete the row
[tableView deleteRowsAtIndexPaths:indexPath
withRowAnimation:UITableViewRowAnimationRight];
// tell the table view that you're done
[tableView endUpdates];
When you call endUpdate the number returned from tableView:numberOfRowsInSection: must be the same as the number at beginUpdate minus the number of rows deleted.
It's pretty easy, the problem lies here:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
The delegate pattern used by apple, means that you're the one responsible on managing the content of the UITableView through its delegates, meaning that, if you delete a row, you're also responsible of deleting the data from the data model.
So, after deleting a row, it would make sense that the number of rows in section would decrease to "9", yet, your function is always returning 10, and thus throwing the exception.
Typically, when using an table, and the contents will change, an NSMutableArray is pretty common, you do something like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayWithStuff count];
}
And then, deleting an object (removeObjectAtIndex:) from the array would automatically update the number of rows.
(Edit: Replied at about the same time as Mike Hay, try following his advice too! I skipped the begin/end Update, because it seems you already read about it)

NSFetchResultController Delegate Error

I have a couple of UIViewControllers that show a table view of my 'User' objects as the section, and 'Site' objects as rows under each section. The Site to User relationship is one-to-many.
At the moment I am showing this data with the following code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:0];
return [sectionInfo numberOfObjects];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
User *sectionUser = (User*)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:section inSection:0]];
return [[sectionUser.sitesToUser allObjects] count];
}
In my NSFetchResultsController I'm just pulling all User objects, nothing special.
Now my issue - whenever I update the results, by removing a section (ie a User) or adding a User whilst this view is also open, I get an NSFetchedResultsController delegate error:
Serious application error. An exception was caught from the delegate of
NSFetchedResultsController during a call to -controllerDidChangeContent:. 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 (1), plus or minus the number of sections inserted or
deleted (0 inserted, 0 deleted). with userInfo (null)
I only get it in the views where I am displaying my data in this way, is User is section, Site/s are rows under each section.
Am I doing something really stupid? I thought it would just remove the section when I delete it etc... Seems to work fine if each User is a ROW in the UITableView - which I believe is the crux of my issue...
I'm using Core Data but my code for numberOfSectionsInTableView: and tableView:numberOfRowsInSection: method is different... I don't know if this can solve your problem but, try to use this code:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rowsNumber = 0;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
rowsNumber = [sectionInfo numberOfObjects];
return rowsNumber;
}
For the sections, I've never used it but try to work on the sectionNameKeyPath argument of the initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName: when you initialize your controller.
You haven't shown your fetchedResultsController delegate calls:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
- (void)controller:didChangeObject:atIndexPath:forChangeType:newIndexPath
- (void)controller:didChangeSection:sectionInfoAtIndex:forChangeType:
These calls are responsible for adding and removing the rows / sections from your table so everything "adds up" as far as the tableview is concerned.
In your case you are kind of "abusing" what a section is so this code could get tricky.
If you are prepared to do without animation you could just implement:
-(void)controllerDidChangeContent:(NSFetchedResultsController*) controller {
[self.tableView reloadData];
}
Daniel.

Deleting a cell from UITableView - error

In my iPhone app, I have a UITableView with the following method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [tableArrays count];
}
Note that tableArrays is a class variable (an NSMutableArray of NSMutableArrays - representing one NSMutableArray for each section in my tableView). Now, that UITableView supports editing, and I have the following method
- (void)tableView:(UITableView *)theTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
...some code...
NSLog(#"tableArrays count is %i",[tableArrays count]);
[tableArrays removeObjectAtIndex:indexPath.section];
NSLog(#"tableArrays is now %#",tableArrays);
NSLog(#"tableArrays count is now %i",[tableArrays count]);
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
So, I run the app and I have two sections in my table view. Row deletion works fine except when I delete the last row in a section. When I delete the last row of section 1, I see the following output, as per the code above:
tableArrays count is 2
tableArrays is now (("Blah1","Blah2"))
tableArrays count is now 1
So clearly, my tableArrays count decreases by one, but I get the following error:
...The number of sections contained in the tableView after the update (1) must be qual to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or dleted (0 inserted, 0 deleted).'
I guess in -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; you are returning the total count of objects and you want to display only one section.
Try returning one in this method.
You should also return [tableArrays count]in -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;.
It should work better like this. Also what is section in
[tableArrays removeObjectAtIndex:section]; ? Did you mean indexPath.section ?
Thank for the comment, so the method that is returning the number of section is correct, but you to do like that to delete a cell.
NSLog(#"tableArrays count is %i",[tableArrays count]);
NSMutableArray *sectionArray = [tableArrays objectAtIndex:indexPath.section];
[sectionArray removeObjectAtIndex:indexPath.row];
//[tableArrays removeObjectAtIndex:indexPath.section];
NSLog(#"tableArrays is now %#",tableArrays);
NSLog(#"tableArrays count is now %i",[tableArrays count]);
The key is to use
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
when you are trying to delete the last row in a section.

Delete UITableView row issue

I'm having a problem that I see a lot of people having... however none of the fixes are working for me. But I did notice something odd happening when I put NSLog commands in the code.
I have the standard delete method:
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.recipes removeObjectAtIndex: indexPath.row];
[tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject: indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
}
recipes is the array that holds the data source.
Theoretically this should work just fine, but I get the error:
invalid number of rows in section 1. The number of rows contained in an existing section after the update (9) 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, 0 deleted).
But I know where this is falling apart, when I add an NSLog into the numberOfRowsInSection, I see that the method is being called twice from the method above.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSLog(#"THIS CALLED TWICE %i",[recipes count] );
return [recipes count];
}
Anyone know what else could cause the numberOfRowsInSection method to fire twice?
Thank you for your time.
After writing this, I saw that I had the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 2;
}
Apparently, numberOfRowsInSection is called for each section... changing the code to:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
Fixed the issue.