Rami Ammoun
Rami Ammoun

Reputation: 857

swipe to delete UITableView cell animation is not working in swift 2

The swipe delete functionality is working fine, but i'm not able to add the animation, i tried everything i can but nothing worked. Whenever I add the code for animation, the app crashes when the cell is deleted. If you load the application again, you will find that the record was deleted, implying that the deletion was successful.

The crash error i'm getting is:

Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) 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).

The animation code blocks i tried were:

        //1
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        //2
        tableView.deleteRowsAtIndexPaths([indexPath],
            withRowAnimation: UITableViewRowAnimation.Automatic)

I also tried to remove those codes and it didn't work too:

fetchAndSetResults() treatmentProtocolsTableView.reloadData()

The entire code in the swift file is here, I commented out the animation blocks, and it works properly.

import UIKit
import CoreData

class Tx_Protocols: UIViewController, UITableViewDataSource, UITableViewDelegate {

    //MARK: declaratoins.
    weak var secureTextAlertAction: UIAlertAction?

    //MARK: Core data related
    var txProtocols = [TreatmentProtocolData]()
    var selectedProtocol : TreatmentProtocolData? = nil

    @IBOutlet weak var treatmentProtocolsTableView: UITableView!


    //When button + is clicked, segue show add tx VC is initiated.
    @IBAction func plusButtonAddTxProtocol(sender: AnyObject) {
        self.performSegueWithIdentifier("showAddTxVC", sender: self)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        fetchAndSetResults()

        treatmentProtocolsTableView.delegate = self
        treatmentProtocolsTableView.dataSource = self
    }

    //When this is used, the data shows with a slight lag, slower.
    override func viewDidAppear(animated: Bool) {
        fetchAndSetResults()
        treatmentProtocolsTableView.reloadData()
    }

    //This is how you catch the app delegate core data fnxnality, GRABBING THE PROPERTY IN APP DELEGATE
    func fetchAndSetResults() {
        let app = UIApplication.sharedApplication().delegate as! AppDelegate
        let context = app.managedObjectContext
        let fetchRequest  = NSFetchRequest(entityName: "TreatmentProtocolData")

        do {
            let results = try context.executeFetchRequest(fetchRequest)
            self.txProtocols = results as! [TreatmentProtocolData]
        } catch let err as NSError {
            print(err.debugDescription)
        }
    }



    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier:"Cell")
        cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
        cell.textLabel!.adjustsFontSizeToFitWidth = true
        cell.textLabel!.font = UIFont.boldSystemFontOfSize(17)

        let treatmentProtocol = txProtocols[indexPath.row]
        cell.textLabel!.text = treatmentProtocol.title
        cell.imageView?.image = treatmentProtocol.getTxProtocolImage()

        return cell
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return txProtocols.count
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        self.selectedProtocol = txProtocols[indexPath.row]
        self.performSegueWithIdentifier("showTxProtocol", sender: self)


    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "showTxProtocol" {
        let detailVC = segue.destinationViewController as! ShowTxProtocolDetailVC
        detailVC.txProtocol = self.selectedProtocol
        }
    }


    //MARK: Edittable table, delete button functionality.
    func tableView(tableView: UITableView,
        canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
            return true
    }

    func tableView(tableView: UITableView,
        commitEditingStyle
        editingStyle: UITableViewCellEditingStyle,
        forRowAtIndexPath indexPath: NSIndexPath) {

            if editingStyle == UITableViewCellEditingStyle.Delete {

                let app = UIApplication.sharedApplication().delegate as! AppDelegate
                let context = app.managedObjectContext
                //1
                let protocolToRemove =
                txProtocols[indexPath.row]

                //2
                context.deleteObject(protocolToRemove)

                //3
                do {
                    try context.save()
                } catch let error as NSError {
                    print("Could not save: \(error)")
                }

//                //1
//                tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
//                //2
//                tableView.deleteRowsAtIndexPaths([indexPath],
//                    withRowAnimation: UITableViewRowAnimation.Automatic)

                fetchAndSetResults()
                treatmentProtocolsTableView.reloadData()

            }
    }


}

I appreciate your help

Upvotes: 1

Views: 4407

Answers (2)

Jacob F. Davis C-CISO
Jacob F. Davis C-CISO

Reputation: 890

Based on the accepted answer by Abhinav, here was my solution (Swift 3). This implemented in my NSFetchedResultsControllerDelegate:

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    self.tableView.beginUpdates()
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    ...
    case .delete:
            // Delete from tableView
            removeFromSetsToDisplayByID(removeThisSet: myObject)
            tableView.deleteRows(at: [indexPath!], with: UITableViewRowAnimation.automatic)
    ...
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    self.tableView.endUpdates()
}

Upvotes: 0

Abhinav
Abhinav

Reputation: 38162

You need to encapsulate inside beginUpdates() and endUpdates(). Also, update your data model that is used to load the table view:

self.tableView.beginUpdates()
self.txProtocols.removeObjectAtIndex(indexPath.row) // Check this out 
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
self.tableView.endUpdates()

Upvotes: 7

Related Questions