Remove Row from UITableView - objective-c

Please read before marking as duplicate....
I am currently trying to remove rows from a UITableView but without any result.
I have a delete button on the cell of the UITableView. Once selected i hope to remove that row.
When the delete button is selected it calls the following method
- (void)didClickOnCellAtIndex:(NSInteger)cellIndex withData:(id)data
{
[self.mainTableView beginUpdates];
NSIndexPath *path = [NSIndexPath indexPathForItem:cellIndex inSection:0];
//
[self.mainTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationFade];
[filteredSubArrayForExpandableSection removeObjectAtIndex:cellIndex];
[appDelegate.managedObjectContext deleteObject:[self.fetchedResultsController objectAtIndexPath:path]];
[appDelegate.managedObjectContext save:nil];
[self.mainTableView endUpdates];
//
// Save the context.
NSError *error;
if (![appDelegate.managedObjectContext save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
// abort();
}
}
The above code will return the following issue
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 (8), 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)
When i reload the app the row still remains.
Ok so the number of rows in this case should be 7!
So logically I added the following code in between the beginUpdates and endUpdates method.
numberOfRows = numberOfRows - 1;
This however returns the following error
-[ViewController deleteRow:]: unrecognized selector sent to instance 0x7a278a00 (lldb)
I can load up the app and the row has been deleted as it should but obviously the app crashed out prior to this.
if i remove the deleteRowsAtIndexPaths method from the didClick method and use the following NSFetchedController Delegate method i get the exact same above issues
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
switch (type) {
case NSFetchedResultsChangeInsert: {
[self.mainTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeDelete: {
[self.mainTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeUpdate: {
//[self configureCell:(TSPToDoCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
NSLog(#"results changed update called");
break;
}
case NSFetchedResultsChangeMove: {
[self.mainTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.mainTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
Update
number of rows method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (canExpand) {
//if can expand section becomes top row for expansion effect thus we need to add 1 to show ALL rows
return numberOfRows + 1;
}
else
{
//if cannot expand section we remove 1 from total
return numberOfRows -1 ;
}
return 0 ;
}
any help regarding this will be greatly received
Thanks

Related

Inserting new item in UITableView leads to invalid number of sections

I'm losing my mind here. Relevant code:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [[self.currentWorkout movements] count];
}
And...
- (void)addMovement:(Movement *)movement{
[self.currentWorkout.movements insertObject:movement atIndex:0];
[self.table beginUpdates];
NSIndexPath *path = [NSIndexPath indexPathForRow:0 inSection:0];
[self.table insertRowsAtIndexPaths:#[path] withRowAnimation:UITableViewRowAnimationTop];
[self.table endUpdates];
}
I have confirmed that insertObject:atIndex: is adding the object; the count of currentWorkout increases by one.
Error: 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (8) must be equal to the number of sections contained in the table view before the update (7), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'
Solved: using wrong method to insert sections.
- (void)addMovement:(Movement *)movement{
[self.currentWorkout.movements insertObject:movement atIndex:0];
NSLog(#"%d",(int)[self numberOfSectionsInTableView:self.table]);
[self.table beginUpdates];
[self.table insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];
[self.table endUpdates];
}
Removing the [self.table beginUpdates]; method call is important because otherwise the table will think it has the number of rows it would get after adding the object before that object is actually added.

Updating UITableView that is managed by NSFetchedResultsController and has 1 extra cell in the first section

Master Detail Application -
I have a UITableViewController that is managed by an NSFetchedResultsController and its delegate methods. I also have one extra cell in the first section of the tableview that has a UIWebView in it that displays an embedded video. This cell is not part of the NSFetchedResultsController. I add it inside of an IF statement when -tableView cellForRowAtIndexPath is called, that checks to see if it's the first section and first row. Everything works great for the most part. I can select a row and display it in the DetailViewController, I can delete items from the tableView, I can change the information in the DetailViewControllerand the tableViewCell labels update with out any problem.
MY ISSUE
The only time I have an issue with the way it is setup is, when I delete the last object from the first section in the tableView.
THE ERROR
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 (3) 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 (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). with userInfo (null)
CODE
numberOfRowsInSection
This is where I check to see if the video is available and if the section is 0 return 1 extra row. else return the object count from the NSFetchedResultsController. I'm assuming this is where my issue is. However, I don't know how to fix it. Because I have it managed by the NSFetchedResultsController delegate methods is there any thing I can do inside of the "type" in the switch statement?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0 && self.videoInfoReceived == YES) {
id <NSFetchedResultsSectionInfo> secInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [secInfo numberOfObjects] + 1;
} else {
id <NSFetchedResultsSectionInfo> secInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [secInfo numberOfObjects];
}
}
NSFetchedResultsController Delegates
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
NSLog(#"didChangeSection - ChangeInsert");
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
NSLog(#"didChangeSection - ChangeDelete");
break;
}
}
- (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:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
NSLog(#"didChangeObject - ChangeInsert");
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
NSLog(#"didChangeObject - ChangeDelete");
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
NSLog(#"didChangeObject - ChangeUpdate");
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
NSLog(#"didChangeObject - ChangeMove");
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
You need more conditional code to check if any index path you're sent is for the first (zero) section. If it is, the FRC is being given the 'wrong' row number because you have told the table view one row count and the FRC understands a different one.
Basically, everywhere you receive an index path from the table view, check the section, if it == 0, create a new index path with the same section and row - 1 and use that to access the objects in the FRC.
Conversely, when you receive an index path from the FRC you need row + 1 before you can use it to ask the table view to insert/delete/move.
In your question you state:
when I delete the last object from the first section in the tableView
Which would lead me to expect the exception to state the following (emphasis added):
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 (0) 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). with userInfo (null)
Because the exception states that the row count is increasing from 2 to 3 without any rows being added, I think that your error is down in the model layer and not in the table view code. Your table view code looks great to me.

crash application while delete section from UITableView

In tableview I have many sections, on the top row of section I have a delete button, I want to do delete section after clicking button but its crashing please help me how can I do this?? here is the code...
//****** adding delete section button
CellButton *_sectionButton = (CellButton*)[cell viewWithTag:10];
_sectionButton.hidden = YES;
_sectionButton.indexPath = indexPath;
[_sectionButton addTarget:self action:#selector(sectionDeleteButton:) forControlEvents:UIControlEventTouchUpInside];
-(void)sectionDeleteButton: (id)sender
{
CellButton *_secbtn = (CellButton*)sender;
NSInteger sectionNumber = _secbtn.indexPath.section;
[self.tableView beginUpdates];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionNumber] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
[self.tableView reloadData];
}
EDIT: The error messsage is:
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1054 2013-07-04 04:25:15.921 estimation[9025:c07] *** 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 (2), plus or minus the number of sections inserted or deleted (0 inserted, 1 deleted)
I guess the numberOfSectionsInTableView: returns the previous number of sections. You must make sure that what you tell the table view to do also happens in the data source. That is, if you delete a section, then the numberOfSectionsInTableView must also return a number one less than before. The same stands for cells and everything else. This is probably described in the error you get.
You have to manage your number of sections in numberOfSectionsInTableview: delegate method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
//#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//#warning Incomplete method implementation.
// Return the number of rows in the section.
return [MLControl shared].arrBuyList.count;//1;//[MLControl shared].arrBuyList.count;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
// [tableView beginUpdates];
NSLog(#"indexPath.row=%ld,indexPath.se=%ld",(long)indexPath.row,(long)indexPath.section);
[[MLControl shared].arrBuyList removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
// [tableView endUpdates];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
I met this problem, but the reason is different from yours.
When I add a section for the tableView, I reload data with this method, this also crash logging like yours:
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
Why do I use this method to reload my table? If I only refresh cells, this works fine, but I forgot I want to change the sections. For more information: What is More reliable way to reload UITableView Data?

Duplication in Uitableview cell?

i got tired from trying all night to solve this issue at the begining my issue is when i add a new data it stored the data into NSMutableArray and in the cellForRowAtIndexPath i show the data, because i have sections so what i did was as follow
if(indexPath.section == 0)
{
for (NSDictionary *dictt in data) {
if ([[dictt objectForKey:#"Type"] isEqualToString:#"Electricity"]) {
NSLog(#"%#",[dictt objectForKey:#"Account"]);
cell.textLabel.text = [dictt objectForKey:#"Account"];
cell.detailTextLabel.text = [dictt objectForKey:#"Type"];
[mainTableView reloadInputViews];
}
}
}
When it comes to final result it show this first
when i add another data it show this
and if their is a data in another section let say water and when i delete that it starts deleting from Electricity section
and here is the delete function:
-(void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
[data removeObjectAtIndex:indexPath.row]; [tableView
deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationTop];
[self saveData];
[mainTableView reloadData]; }
You are using the same array for all of your sections, so you definitely want to get a better data structure in place. In your delete function you aren't checking the section of the tableview, so that's why it's deleting from the wrong section.
Basically, you want to create an array that contains arrays of data for each section, so that in your master array the object at index 0 is the array for section 0 etc.

Core Data: Automatically select next table row when record deleted

I'm working on a Core Data driven iPad app with a split view controller. Just imagine the iPad Mail app and you'll be on the right track. When I select a record in the Root View Controller, the details display in the DetailViewController.
On the Detail View, I have a delete button. When clicked, it tells its Core Data context to delete the current object. It performs the delete correctly and the row disappears from the RootViewController, as it should.
How can I get the RootViewController to automatically select the row after the row that was deleted so it subsequently displays the details in the detail view? (Or automatically select the previous row if the deleted row was the last row?)
If you use an NSFetchedResultsController to manage the table then you can use it's delegate methods to respond to changes.
Use the controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: delegate method and check for the NSFetchedResultsChangeDelete change type.
You can see if the indexPath matches the currently selected rows in your table and then act upon that.
OK. I think I figured it out. Here's what I did. (Please chime in if you see a better way.)
First, I defined a new NSInteger ivar, lastSelectedRow.
Next, I changed
case NSFetchedResultsChangeDelete: {
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
to
case NSFetchedResultsChangeDelete: {
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
lastSelectedRow = indexPath.row;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:0];
if (lastSelectedRow == [sectionInfo numberOfObjects]) {
--lastSelectedRow;
}
break;
}
Then I changed
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
to
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
if (lastSelectedRow > -1) {
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:lastSelectedRow inSection:0];
[self.tableView selectRowAtIndexPath:newIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
[self tableView:self.tableView didSelectRowAtIndexPath:newIndexPath];
lastSelectedRow = -1;
}
}
Und voila!!
As an added bonus I can now store the last selected row when my app is terminated, and I can go right back to it next time it launches.