Delete from UITableView using an NSFetchedResultsController - objective-c

I have a UITableView which is populated through a NSFetchedResultsController. When the user slides an item to the right I have a delete button appearing so that he/she can remove the object using the following approach:
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//perform similar delete action as above but for one cell
XMPPUserCoreDataStorageObject *user = [[self fetchedResultsController] objectAtIndexPath:indexPath];
NSLog(#"User delete: %#", [user displayName]);
//delete from fetchController
NSArray *sections = [[self fetchedResultsController] sections];
int userStatus = [[user sectionNum] intValue];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
But when I do that there is an exception because I am not updating the model which is my fetchedResultsController. The question is how to remove from the fetchedController using the indexPath that I have from the commitEditingStyle ?

Since you are using NSFetchedResultsController you only need to delete the object from the context, and it will pick up the change and delete the rows for you.
So remove the deleteRowsAtIndexPaths: line and call this instead:
[self.fetchedResultsController.managedObjectContext deleteObject:user];

Have a look at this Answer , what you have to do is delete an NSManagedObject from your database as well as from the NSFetchedResultsController.

Related

Multiple selection to array.

My problem is that I load players from database to UITableView and i want to store this players in Array but I provide multiple selection. Next I want so save this information to DB. I have done DB layer so just need information how to store this multiple selected players to array.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.cellSelected = [NSMutableArray array];
self.selectedItems = [NSMutableArray array];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//Cell Initialisation here
if ([self.cellSelected containsObject:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//if you want only one cell to be selected use a local NSIndexPath property instead of array. and use the code below
//self.selectedIndexPath = indexPath;
//the below code will allow multiple selection
if ([self.cellSelected containsObject:indexPath])
{
[self.cellSelected removeObject:indexPath];
[self.selectedItems removeObject:[self.selectedItems indexOfObject:[self.dataArray objectAtIndex:indexPath.row]]];
}
else
{
[self.cellSelected addObject:indexPath];
[self.selectedItems addObject:[self.dataArray objectAtIndex:indexPath.row]];
}
[tableView reloadData];
}
Where your data array hold data from database (Whole players information). selectedItems array contains the details of selected player info.
Hope this helps :)

Delete cell from UITableView not from Array

I learn how work with CoreData and Table view controller. I learn from a book and there is using this function for display content of TableViewControoler.
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if(debug == 1){
NSLog(#"Running %# '%#'",self.class, NSStringFromSelector(_cmd));
}
static NSString *cellIndetifier = #"Class Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndetifier
forIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryDetailButton;
SchoolClass *class = [self.frc objectAtIndexPath:indexPath];
NSMutableString *title = [NSMutableString stringWithFormat:#"%#", class.name];
[title replaceOccurrencesOfString:#"(null)"
withString:#""
options:0
range:NSMakeRange(0, [title length])];
cell.textLabel.text = title;
return cell;
}
Now I would like delete a record(cell) from table view controller and from coreData. I don't know what is the simplest way, but I found this:
I have one button "delete" and the button has this IBA action.
-(IBAction)deleteClassAction:(id)sender{
[self.tableView setEditing:YES animated:YES];
}
When I press the button I see something like this:
http://i.stack.imgur.com/3VAyp.png
Now I need delete the selected cell. I found some code and I prepared skeleton for deleting. But I need advice in two things.
I don't know how get the ID of deleting item and how to write code for filter - viz. comment: // the code that filters the object that has been marked for deleting cell from tableView
I don't know how delete the cell from tableView. I found the solution, which is under the comment, but it has a error:
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) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Here is the code with comments.
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
// deleting data from CoreData database
CoreDataHelper *cdh = [(AppDelegate *) [[UIApplication sharedApplication] delegate] cdh];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"User"];
NSPredicate *filter = // the code that filters the object that has been marked for deleting cell from tableView"
[request setPredicate:filter];
NSArray *fetchedObjects = [cdh.context executeFetchRequest:request error:nil];
for(SchoolClass *class in fetchedObjects){
[_coreDataHelper.context deleteObject:class];
}
// deleting cell from tableView
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
[self.tableView reloadData];
}
You just need to delete the object from your NSFetchedResultsController. You might also then want to save the NSManagedObjectContext. The fetched results controller will then update the table view assuming you've implemented the delegate methods as described in the docs.
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
// deleting cell from fetched results contoller
if (editingStyle == UITableViewCellEditingStyleDelete) {
SchoolClass *class = [self.frc objectAtIndexPath:indexPath];
[[class managedObjectContext] deleteObject:class];
}
}

Swipe to delete

I Have a tableview and a cell in it :D with disclosure accessory selected. I created all ui elements with storyboard.
(didn't put a view in content of the cell)
I'm using SWTableViewCell to implement just swipe-to-delete, but everything seems work fine except when i put a breakpoint on to a method
#pragma mark -SWTableViewDelegate-
-(void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index
{
NSIndexPath *path=[self.table indexPathForCell:cell];
[anArray removeObjectAtIndex:path.row];
[self.table deleteRowsAtIndexPaths:#[path] withRowAnimation:UITableViewRowAnimationRight];
}
and this will help you to understand what i simply done here
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
SWTableViewCell *cell=(SWTableViewCell*)[tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
if (!cell)
{
NSMutableArray *rightUtilityButtons = [NSMutableArray new];
[rightUtilityButtons sw_addUtilityButtonWithColor:[UIColor colorWithRed:1.0f
green:0.231f
blue:0.188
alpha:1.0f]
title:#"Delete"];
cell = [[SWTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell" containingTableView:_table leftUtilityButtons:nil rightUtilityButtons:rightUtilityButtons];
}
NSMutableDictionary *deDict=[[NSMutableDictionary alloc]initWithDictionary:[anArray objectAtIndex:indexPath.row]];
cell.textLabel.text= [deDict objectForKey:#"Name"];
return cell;
}
Yet, I don't get any error, because when i try to swipe on simulator, it simply does not work..
No need to use SWTableViewCell just Override following built-in methods of UITableView delegate for support conditional editing & swipe to delete
for conditional editing -- Optional --
//Override this method only if you are going to be returning NO for some items.
//By default, all items are editable.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return YES if you want the specified item to be editable.
return YES;
}
for swipe to delete
// Override to support editing the table view.
// for swipe to delete
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//add code here for when you hit delete
}
}

What would be the most elegant way to implement this without multiple inheritance in objective-c?

Basically I have classes of objects whose main function is to act as tableView Delegate.
I want to add that to some superclass. Of course there is only 1 superclass and I want flexibility. What about if latter I want to add this capability to other classes at will?
Basically these are codes used to handle tables where users can delete or rearrange rows, etc.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSAssert(false, #"Should be called at child View");
return nil;
}
-(Class) classBookmarked
{
assert(false);
return nil;
}
-(void) setEditing:(BOOL)editing animated:(BOOL)animated
{
[self.delegate.tvDelegated setEditing:editing animated:animated];
if (!editing)
{
NSArray * newIds = _arManagedObjectArray.convertArrayOfNSManagedObjectToItsDefaultSelector;
[self varManagedObjectArrayUpdated];
[BGBookmarkStorer vReportBookmarkStatusToServer:newIds Flag:#"update" withClass:self.classBookmarked];
}
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = indexPath.row;
[self deleteARow:row];
}
-(void)deleteARow:(NSUInteger) row
{
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row inSection:0];
[_arManagedObjectArray removeObjectAtIndex:row];
[self.delegate.tvDelegated deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self varManagedObjectArrayUpdated];
}
-(void) varManagedObjectArrayUpdated
{
[self.bookmarkStorer vUpdateBookMarkIDwithArray:_arManagedObjectArray];
[self.delegate vUpdateNumberOfStuffs];
}
-(BGBookmarkStorerForPlacesandCatalog *) bookmarkStorer
{
assert(false);
return nil;
}
- (NSArray*) theBookmarkedIDs
{
return self.bookmarkStorer.bookmarkedIDs;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
NSMutableArray * mutableBusinessBookmarked= _arManagedObjectArray;
NSManagedObject *bizOrCatToMove = mutableBusinessBookmarked[sourceIndexPath.row];
[mutableBusinessBookmarked removeObjectAtIndex:sourceIndexPath.row];
[mutableBusinessBookmarked insertObject:bizOrCatToMove atIndex:destinationIndexPath.row];
//_arManagedObjectArray=mutableBusinessBookmarked;
[self varManagedObjectArrayUpdated];
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
Create a subclass of uitableviewcontroller. Implement all your functions there. Use it as a superclass for all your view controllers.
If I understand correctly, you want a practical place where you can put that code and reuse in your table view controllers, right?
If so, simply create a category on UITableViewController and put your code there ;-)

How to keep selection of untouched cells intact?

I have a tableView where I select cells and add the data in an array, I also have the option of swiping and then deleting a particular cell which eventually deletes the data from the array.
The problem is that once I delete a row, I lose all my selection state after I reload the table,
For that I checked again with the selection array and reselected all these cells,
BUT I am stuck at one place, Much before I actually delete a cell and reload the tableView, as soon as I swipe over a cell, selection state of all other cells also go away.
NOTE: I have two arrays, one with list of itmes to be displayed in the tableView and one with the selected items.
Here is some code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.contactList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
cell.selectionStyle = UITableViewCellSelectionStyleGray;
[cell.textLabel setTextColor:[UIColor colorWithRed:103.0/255.0 green:103.0/255.0 blue:103.0/255.0 alpha:1.0]];
[cell.textLabel setFont:[UIFont fontWithName:#"ITCAvantGardeStd-Bk" size:14.0]];
if (![[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"nickName"] isEqualToString:#""])
cell.textLabel.text = [NSString stringWithFormat:#"%#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"nickName"]];
else
cell.textLabel.text = [NSString stringWithFormat:#"%# %#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"firstName"],[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"lastName"]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Selected cell index==>%d\n",indexPath.row);
//NSString *emailID = [NSString stringWithFormat:#"%#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"email_key"]];
NSLog(#"emailID==>%#\n",[self.contactList objectAtIndex:indexPath.row]);
[self.emailShareList addObject:[self.contactList objectAtIndex:indexPath.row]];
//[self.emailShareList insertObject:emailID atIndex:indexPath.row];
NSLog(#"Array value==>%#\n",self.emailShareList);
//[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"deSelected cell index==>%d\n",indexPath.row);
NSString *emailID = [NSString stringWithFormat:#"%#",[[self.contactList objectAtIndex:indexPath.row] objectForKey:#"email_key"]];
NSLog(#"emailID==>%#\n",emailID);
[self.emailShareList removeObject:[self.contactList objectAtIndex:indexPath.row]];
NSLog(#"deSelect row Array value==>%#\n",self.emailShareList);
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
if(indexPath.row != 0)
{
NSString *contactID = [[self.contactList objectAtIndex:indexPath.row] objectForKey:#"contactId"];
NSLog(#"content on delete row==>%#\n",contactID);
[self.contactList removeObjectAtIndex:indexPath.row];
[self deleteContactToServer:contactID];
}
}
[contactTableView reloadData];
for (int i = 0; i < [self.emailShareList count]; i++)
{
for (int j = 0; j < [self.contactList count]; j++)
{
if([[[self.contactList objectAtIndex:j] valueForKey:#"email"] isEqualToString: [[self.emailShareList objectAtIndex:i] valueForKey:#"email"]])
{
NSIndexPath *path1 = [NSIndexPath indexPathForRow:j inSection:0];
[contactTableView selectRowAtIndexPath:path1 animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellEditingStyle style = UITableViewCellEditingStyleNone;
if(indexPath.row != 0)
style = UITableViewCellEditingStyleDelete;
return style;
}
When you delete an item, you don't necessary have to reload the entire tableview. You could use the – deleteRowsAtIndexPaths:withRowAnimation: method to just remove the cell in question (along with an according model update). This will probably retain your selection.
To keep your selections when entering editing mode (swipe for delete is a special case of editing mode as well) you nee to do two things:
First, enable allowsSelectionDuringEditing on your tableView:
self.tableView.allowsSelectionDuringEditing = YES;
Second, create a UITableView subclass and override setEditing:animated: like this:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
NSArray *indexPaths = self.indexPathsForSelectedRows;
[super setEditing:editing animated:animated];
for (NSIndexPath *ip in indexPaths) {
[self selectRowAtIndexPath:ip animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
Personally, I would rather use some sort of custom selection mechanism, when selections are important from a model point of view. I would create a custom cell subclass, add a selection property to it let it change the cell styling accordingly. The build-in features that affect regular table view selections won't cause problems with such an approach.
Here is an additional method of preserving table selections in and out of edit mode without having to subclass UITableView. Add the following to your UITableViewControllerView.
Within viewDidLoad add:
self.tableView.allowsSelectionDuringEditing = YES;
Then override setEditing:animated:
- (void)setEditing:(BOOL)editing animated:(BOOL)animate
{
NSArray *selectedIndexPaths = [self.tableView indexPathsForSelectedRows];
[super setEditing:editing animated:animate];
for (NSIndexPath *selectedIndexPath in selectedIndexPaths) {
[self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}