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.
Related
I have a multi-section tableview. In edit mode I allow rows to be moved from one section to another. Once the final row is removed from one section I delete that section. So I am using deleteSection inside moveRowAtIndexPath.
When the final item is moved from the section, the section header disappears as planned. But there is a very strange animation bug, where the moved row seems to 'merge' with the row it is dropped above, and an empty row is displayed at the bottom of the 'to' section (probably because the numberOfRows for that section is correct, but 2 rows are in the same position). Even stranger, when I click the reorder control for this row (not moving the item, simply touching and releasing), the two items 'unmerge'.
I have posted a video demonstrating this.
I have tried wrapping my data changes and view changes in begin/end updates, but to no avail.
I have uploaded a test project here, and I will also post the code below. A couple of points:
I have tried to replicate my data source's format in the demo project, in case this is where the problem originates. The key thing is that my source is a composite array of two other arrays (though I can't see why this would be an issue).
To see the behavior in question, move the two rows in the bottom section, up into the top section. Don't drop them in the last row on the top section though, since this seems to work ok.
Moving rows the other way, from the top section to the bottom section, is buggy in this demo project.
Code (all of this is in the demo project):
I set up my arrays in loadView:
- (void)loadView{
array1 = [NSMutableArray array];
[array1 addObject:#"test 0"];
[array1 addObject:#"test 1"];
[array1 addObject:#"test 2"];
array2 = [NSMutableArray array];
[array2 addObject:#"test a"];
[array2 addObject:#"test b"];
[super loadView];
}
I also have a method that returns a combination of these arrays - this is used as the data source:
- (NSMutableArray *)sourceArray{
NSMutableArray *result = [NSMutableArray array];
if (array1.count > 0) {
[result addObject:array1];
}
if (array2.count >0) {
[result addObject:array2];
}
return result;
}
Which allows for very simple number of rows/sections:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return self.sourceArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[self.sourceArray objectAtIndex:section] count];
}
Standard Cell/Header formatting:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = [[self.sourceArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [NSString stringWithFormat:#"Section %i", section];
}
This is where I do the magic
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
I notice that the row that comes from the to-be-deleted section is the one that disappears until you retouch the order control.
I suspect that when this datasource method is called by the tableview, its state is still in the middle of performing the move, so calling 'deleteSections' will make the table try and delete the row you're moving. It's not so much of a merge as the fact that it's fading away at the same rate as the section header, and the one below it is just scooting back up to fill the space.
Tapping the control causes the table view to rejigger itself and realize that the row isn't actually gone.
to try and work around this, try running the deletion in the next runloop, via a dispatch call, like:
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
dispatch_async(dispatch_get_main_queue(), ^() {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
});
}
this will cause the deletion to run on the main thread still, but allow the 'moveRow' and whatever call stack it happens to be in finish up its logic before the deletion call
Your problem is in the animation. One is being done while another is not yet finished (moving & deleting animation) causing one cell to be drawn upon the other. You can verify this by moving the cells around again. The correct order will then be displayed. According to Apple's docs on the UITableView:
Note: The data source should not call setEditing:animated: from within its implementation of tableView:commitEditingStyle:forRowAtIndexPath:. If for some reason it must, it should invoke it after a delay by using the performSelector:withObject:afterDelay: method.
Therefore to fix this, do this to your code:
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self performSelector:#selector(someMethod:) withObject:fromIndexPath afterDelay:1.0];
}
- (void) someMethod:(NSIndexPath *) fromIndexPath {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
Should work fine. Just change the delay to something shorter that suites you.
On the off chance that your rows or what's inside them can take focus, have you checked that you have called resignFirstResponder or [view endEditing:YES]? We saw this when we used text fields and (IIRC it was iOS 4 version dependent too) left the focus in one of the fields.
You have to reload the tableview after deleting the section. Try this code.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
}
}
Swap the order of fromArray and toArray in your code. If the item has a retain count of 1 prior to removing it from the array, it will have a retain count of 0 before adding it to toArray.
If you swap the order, the item will go from retain count of 1 to 2 then back to 1 when the remove is complete.
I think the UITableViewRowAnimationFade animation is interfering with the UITableViewCell move animation. One thing you can try is to delay the section deletion a little bit late in order for the cell move row animation to finish.
Try replace your code with the following code.
-(void)deleteSection:(NSIndexSet*)indexSet
{
[self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self performSelector:#selector(deleteSection:) withObject:[NSIndexSet indexSetWithIndex:fromIndexPath.section] afterDelay:1.0];
// [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
a solution that lost animation on last row :
if([listOfItemsOnTransaction count]==indexPath.row){
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
}else
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
withRowAnimation:UITableViewRowAnimationFade];
}
After days of research and re-coding I am pretty much stumped. My goal is to get a test app running with a single tableview populated from two separate fetchedResultControllers.
I have a series of items on a shopping list, each with a department and a boolean 'collected' flag. Uncollected items should be listed by department, followed by a single section containing all collected items (regardless of department). As a user checks off uncollected items, they should move down to the 'collected' section. If s/he un-checks a collected item, it should move back into its correct department.
To achieve the first part (uncollected items), I set up a simple fetchedResultsController that fetches all items where collected = NO, and sectioned the results by department:
- (NSFetchedResultsController *)firstFRC {
// Set up the fetched results controller if needed.
if (firstFRC == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// fetch items
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// only if uncollected
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(collected = NO)"];
[fetchRequest setPredicate:predicate];
// sort by name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// fetch results, sectioned by department
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"department" cacheName:nil];
aFetchedResultsController.delegate = self;
self.firstFRC = aFetchedResultsController;
}
return firstFRC;
}
I set the number of rows, sections, and section headers as follows:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return firstFRC.sections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[firstFRC.sections objectAtIndex:section] numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[[firstFRC sections] objectAtIndex:section] name];
}
I also use the boilerplate controllerWillChangeContent, didChangeObject, and controllerDidChangeContent to add/remove cells as the FRC's change.
For brevity, I won't include the code for displaying the cell, but I essentially pull the correct item based on the cell's index path, set the text/subtitle of the cell, and attach one of two checkmark images, depending on whether the item is collected or not. I also wire up this image (which is on a button) to toggle from checked to unchecked when it is touched, and update the item accordingly.
This part all works fine. I can view my list of items by department, and when I mark one as collected, I see it drop off the list as expected.
Now I attempted to add the bottom section, containing all of the collected items (in a single section). First I set up a second fetchedResultsConroller, this time to fetch only uncollected items, and without sectioning. I also had to update the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections - add one for 'collected' section
return firstFRC.sections.count + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section
if (section < firstFRC.sections.count) {
return [[firstFRC.sections objectAtIndex:section] numberOfObjects];
}
else {
return secondFRC.fetchedObjects.count;
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section < firstFRC.sections.count) {
return [[[firstFRC sections] objectAtIndex:section] name];
}
else{
return #"Collected";
}
}
I then updated the cellForRowAtIndexPath in a similar fashion, so that the item I retrieve comes from the right FRC:
Item *item;
if (indexPath.section < firstFRC.sections.count) {
item = [firstFRC objectAtIndexPath:indexPath];
}
else {
item = [secondFRC objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
}
[[cell textLabel] setText:[item name]];
…rest of cell configuration
This works great when I launch. The tableview displays exactly as anticipated:
The Problem (finally)
The (first) problem comes when I select the checkmark for an uncollected item. I expect the item to be removed from the department it is listed under, and moved to the 'collected' section. Instead, I get:
CoreData: error: Serious application error. An exception was caught
from the delegate of NSFetchedResultsController during a call to
-controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section
after the update (2) must be equal to the number of rows contained in
that section before the update (2), 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). with userInfo (null)
If I attempt the opposite, then I receive a different error:
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 ..
1]'
I suspect that in both cases there is a problem with consistency with the number of sections/rows in the FRCs and the tableview when an item moves from one FRC to the other. Although that second error makes me think there is maybe a simpler problem related to my retrieval of items.
Any direction or ideas would be appreciated. I can provide more of my code if it would help, and have also created a small test app with a single view to demonstrate the issue. I can upload it if necessary, but mostly I wanted to test the issue in a small scale sandbox.
Update - additional code requested
As requested, this is what happens when a checkmark is touched:
- (void)checkButtonTapped:(id)sender event:(id)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
if (indexPath != nil)
{
[self tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
}
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
Item *item;
if (indexPath.section < firstFRC.sections.count) {
item = [firstFRC objectAtIndexPath:indexPath];
}
else {
item = [secondFRC objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIButton *button = (UIButton *)cell.accessoryView;
if (![item collected]) {
[item setCollected:YES];
[button setBackgroundImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
}
else if ([item collected]){
[item setCollected:NO];
[button setBackgroundImage:[UIImage imageNamed:#"unchecked.png"] forState:UIControlStateNormal];
}
NSError *error = nil;
if (![item.managedObjectContext save:&error]) {
NSLog(#"Error saving collected items");
}
}
Well, I think I might have cracked it. As is often the case, stripping it down and reading just a few comments pointed me in the right direction.
When I get to the delegate method controllerDidChangeObject, I attempt to insert a row at the indexPath provided (for the 'checked' item). Except that when inserting into my additional section, this indexPath has no awareness of the fact that there are a bunch of other sections before it. So it receives section 0 and attempts to insert there. Instead, if the indexPath comes from the second FRC, I should be incrementing the section number by the number of sections in the first FRC's table. So, I replaced:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
with
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
if ([controller.sectionNameKeyPath isEqualToString:#"department"]) {
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else {
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:newIndexPath.row inSection:firstFRC.sections.count]] withRowAnimation:UITableViewRowAnimationFade]; }
break;
I will need to do this for each of insert, delete, update etc. I will mark this as the answer after I have validated this and to allow time for other comments.
The errors you are seeing mean that your UITableView reloads itself before BOTH your NSFetchedResultsControllers do. The codes you posted are probably correct. I suspect that the problem is in one of the NSFetchedResultsControllerDelegate methods.
When deleting a row from Uitableview using commitEditingStyle, my app crashes with this error.
Assertion failure in -[UITableView _endCellAnimationsWithContext:],
/SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1046 .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 (2)
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, 1 deleted) and plus or minus the number
of rows moved into or out of that section (0 moved in, 0 moved out).
This is my code:
- (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// If row is deleted, remove it from the list.
if (editingStyle == UITableViewCellEditingStyleDelete) {
order *OrderObj= [appDelegate.orderArray objectAtIndex:[indexPath row]];
[appDelegate removeitem:OrderObj];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
} }
Try to change these lines:
order *OrderObj= [appDelegate.orderArray objectAtIndex:[indexPath row]];
[appDelegate removeitem:OrderObj];
to:
[appDelegate.orderArray removeObjectAtIndex:[indexPath row]]; // assuming orderArray is NSMutableArray
Your tableView:numberOfRowsInSection: is returning bad value after the update. Check the value returned before delete and after delete. It must be decreased by 1.
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.
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.