简体   繁体   中英

Deleting the wrong row in a tableView

I'm a beginner in Swift and tried to build an app where an array saves the items in a tableView by Core Data. That works. But what won't work is to delete the right row by swiping.

It is deleted the right row first. But when I go back to the app, it is the row above the initial selected row deleted/not shown anymore.

Anybody there who could give an advice?

Here is the code:

import UIKit
import CoreData

var shoppingList: [NSManagedObject] = [ ]

class ShoppingList_1: UIViewController, UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

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

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let item = shoppingList[indexPath.row]
        let cell = Cell.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = item.value(forKeyPath: "itemName") as? String

        cell.detailTextLabel?.text = "\(indexPath.row)"
        return cell
    }

    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let itemTmp = shoppingList[sourceIndexPath.row]
        shoppingList.remove(at: sourceIndexPath.row)
        shoppingList.insert(itemTmp, at: destinationIndexPath.row)
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete
        {
            shoppingList.remove(at: indexPath.row)
            Cell.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
             //Cell.reloadData()

            guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
            let managedContext = appDelegate.persistentContainer.viewContext
            managedContext.delete(shoppingList[indexPath.row])

            do {
                try managedContext.save()
            }   catch let err as NSError {
                print("12345", err)
            }
        }
    }

    @IBOutlet weak var AddButton: UIButton!
    @IBOutlet weak var AddItem: UITextField!
    @IBOutlet weak var Cell: UITableView!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
        let managedContext = appDelegate.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Item")
        do {
            shoppingList = try managedContext.fetch(fetchRequest)
        } catch let err as NSError {
            print("Failed to fetch items", err)
        }
    }
}

Your issue is you are passing the wrong object to managedContext.delete because you access the element by index after you remove the item from the array. In fact, if you tried to delete the last row your app would crash.

You should also only update your local data model and the table if you successfully remove the value from Core Data.

You should update your commit editingStyle method as follows:

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete
    {
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}

        let managedContext = appDelegate.persistentContainer.viewContext
        managedContext.delete(shoppingList[indexPath.row])

        do {
            try managedContext.save()

            shoppingList.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
        } catch let err as NSError {
            print("12345", err)
        }
    }
}

This happens because you first delete the element from the array, then remove the cell and then finally save the array. I would suggest firstly deleting the element, then saving the array and finally removing the cell from the tableView. In order to ensure that your application always follows this order, create a delete() function with a closure. All the database removal should be done in the actual function, and the tableViewCell must be removed in the closure, so that you are sure that it gets removed after everything else is correctly done.

This is the function:

func deleteRows(closure: () -> ()) {
    shoppingList.remove(at: indexPath.row)
    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
    let managedContext = appDelegate.persistentContainer.viewContext
    managedContext.delete(shoppingList[indexPath.row])

    do {
        try managedContext.save()

    }   catch let err as NSError {
        print("12345", err)
        return
    }
    closure()
}

This is you calling it:

deleteRows {
    tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM