Search code examples
iosuitableviewcore-datansfetchedresultscontroller

Deleting Core Data from a UITableView within a UIViewController


I have a UITableView within a UIViewController so that I can have a UILabel below the table. In doing so, I have had difficulties in adding an Edit/Done button. I couldn't do the traditional way, so I had to do a work around using the following idea:

1)Create the edit button up the top left on viewdidload:

self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(editbutton)];

2)Create code so that upon clicking the edit button, the tableview becomes editable, and it changes the title to done. Then upon clicking done, it goes back to saying edit.

-(IBAction)editbutton{
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donebutton)];
[tableView setEditing:YES animated:YES];

}

-(IBAction)donebutton{
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(editbutton)];

[tableView setEditing:NO animated:YES];

}

This part is all OK. I just have put it in for completeness. The tableview becomes editable when click the edit button, and i can click done and it goes back to normal. My problem is clicking the delete button (after clicking the Red minus button next to a row) doesn't delete the row. I have tried the following code:

NOTE: 1)context has been declared in the .h file as: @property (nonatomic, retain) NSManagedObjectContext *context; and synthesized in the .m file. 2)I have declared the @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController in the .h file and then the @synthesize fetchedResultsController = _fetchedResultsController in the .m file

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {

    // Delete the managed object for the given index path
    [context deleteObject:[_fetchedResultsController objectAtIndexPath:indexPath]];

    // Save the context.
    NSError *error = nil;
    if (![context 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. 
         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}   
}

EDIT:

Ok, I have found a bit of a solution. I used the following code to delete my core data:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
    // Delete the managed object for the given index path
    NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
    [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

    // Save the context.
    NSError *error = nil;
    if (![context 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. 
         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
    [self fetchedresults];
    [self.tableView reloadData];

}   
}

My new problem is that when i click delete. The row is deleted but still remains there with the red minus button on a blank row. I can still click on the row too (which normally edits the data) but there is no data to load.

EDIT 2:

I forgot to add, to get it to work, i added this:

- (NSFetchedResultsController *)fetchedResultsController{

if (_fetchedResultsController != nil) {
    return _fetchedResultsController;
}

// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Record" inManagedObjectContext:context];
[fetchRequest setEntity:entity];

// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];

// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"activity" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

[fetchRequest setSortDescriptors:sortDescriptors];

// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:@"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;

NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {


    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

return _fetchedResultsController;
}

EDIT 3:

This is what fetched results does:

- (void)fetchedresults {

NSManagedObjectContext *moc = [self context];
NSEntityDescription *entityDescription = [NSEntityDescription
                                          entityForName:@"Record" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];


NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
                                    initWithKey:@"activity" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];

NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil)
{
    // Deal with error...
}

float tot = [[array valueForKeyPath:@"@sum.cpdhours"] floatValue]; 
totalhours.text =  [NSString stringWithFormat:@"%.1f", tot];


}

Solution

  • Are you using an NSFetchedResultsController? If not, try it and you shouldn't worry about reloading the table at all.


    Also, there's absolutely no need to use a custom button. Just stick with the .editButtonItemand override -setEditing:animated:.

    - (void)setEditing:(BOOL)editing animated:(BOOL)animated
    {
        [super setEditing:editing animated:animated];
        [self.tableView setEditing:editing animated:animated];
    }