horst horst - 8 months ago 45
Swift Question

delete UITableViewCell with animation (error/crash)

I'd like to delete a

& a CoreData Object from a
with an animation. I wrote this code for doing it:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

let mySelectedCell:UITableViewCell = tableView.cellForRowAtIndexPath(indexPath)!

let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate

let managedContext = appDelegate.managedObjectContext!

let fetchRequest = NSFetchRequest(entityName:"Person")

mySelectedCell.backgroundColor = green
mySelectedCell.detailTextLabel?.text = "Done"
mySelectedCell.accessoryType = UITableViewCellAccessoryType.Checkmark

let person = people[indexPath.row]
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Right)

I don't know why but the app is crashing if I select a cell, which I want to delete.

2015-03-29 18:00:10.776 MyApp[3001:79507] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-3318.93/UITableView.m:1582

Crashing line:
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Right)

Does someone knows what I've done wrong?



var numberItems:Int = people.count

self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Right)
numberItems -= [indexPath].count // something like [indexPath].count


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

application.applicationIconBadgeNumber = people.count
return people.count

New error:

2015-03-29 19:14:15.972 MyApp[3389:89566] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-3318.93/UITableView.m:1582

in this line of code:


If the table view displays Core Data objects using a fetched results controller then the only thing you have to do to delete an object is to delete the object from the managed object context:

let context = self.fetchedResultsController.managedObjectContext
context.deleteObject(self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject)

and of course save the context to make the change permanent.

Updating the table view is then done "automatically" by the fetched results controller delegate methods

func controllerWillChangeContent(controller: NSFetchedResultsController)
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType)
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)
func controllerDidChangeContent(controller: NSFetchedResultsController)

You'll find a typical implementation of these methods in the NSFetchedResultsControllerDelegate documentation, if you haven't implemented them already.

Also you should not have a "copy" of the fetched objects in your people array. The fetched results controller is directly used in the table view data source methods, for example:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return self.fetchedResultsController.sections?.count ?? 0

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sectionInfo = self.fetchedResultsController.sections![section] as NSFetchedResultsSectionInfo
    return sectionInfo.numberOfObjects

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
    self.configureCell(cell, atIndexPath: indexPath)
    return cell

If you create a fresh "Master-Detail + Core Data Application" in Xcode then you'll get all the necessary template code that you need.