【问题标题】:Assertion failure UITableView: delete object which a relationship to another object断言失败UITableView:删除与另一个对象有关系的对象
【发布时间】:2020-04-06 23:53:26
【问题描述】:

我尝试删除与某人相关的礼物。现在,当运行程序并尝试删除一个存在的对象时,会发生错误:

HappyBirthdayMom[7907:369895] * -[UITableView _Bug_Detected_In_Client_Of_UITableView_Invalid_Number_Of_Rows_In_Section:] 中的断言失败,/BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3900.12.16/UITableView.m: 2406 2019-12-13 14:34:55.941744+0100 HappyBirthdayMom[7907:369895] * 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:第 0 节中的行数无效。更新后现有节中包含的行数 (2) 必须等于更新前该节中包含的行数 (2),加上或减去从该节中插入或删除的行数(0 插入,1 删除) 加上或减去移入或移出该部分的行数(0 移入,0 移出)。'

我真的不知道如何解决这个问题——giftArray 的计数应该返回行数。


import UIKit
import CoreData

class PresentViewController: UITableViewController {

    @IBOutlet weak var deleteButton: UIBarButtonItem!

    var managedContext : NSManagedObjectContext?

    var person: Person?

    var giftArray: [Geschenk] {
        return person?.geschenke?.allObjects as? [Geschenk] ?? [Geschenk]()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }


    // MARK: - Table View

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

    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "GeschenkCell", for: indexPath)

        let present = giftArray[indexPath.row]

        cell.textLabel?.text = present.title

        //Tenary operator ==>
        // value = condidtion ? valueIfTrue : valueIfFalse
        cell.accessoryType = present.isOwned ? .checkmark : .none

        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        //print(itemArray[indexPath.row])


        giftArray[indexPath.row].isOwned = !giftArray[indexPath.row].isOwned

        saveGift()

        tableView.deselectRow(at: indexPath, animated: true)
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            managedContext!.delete(giftArray[indexPath.row] as NSManagedObject)
            tableView.deleteRows(at: [indexPath], with: .fade)

            do {
                try managedContext!.save()
            } catch {
                ("Error delete gift \(error)")
            }
            self.reloadPerson()
        }
    }

    // MARK: - Actions

    @IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
        var textField = UITextField()

        let alert = UIAlertController(title: "Neues Geschenk hinzufügen", message: "", preferredStyle: .alert)

        let action = UIAlertAction(title: "Hinzufügen", style: .default) { (action) in

            let newGift = Geschenk(context: self.managedContext!)
            newGift.title = textField.text!
            newGift.isOwned = false
            newGift.owner = self.person

            self.saveGift()
        }

        let actionCancel = UIAlertAction(title: "Abbrechen", style: .cancel) {(action) -> Void in

        }

        alert.addTextField{ (alertTextfield) in
            alertTextfield.placeholder = "Neues Geschenk hinzufügen"
            textField = alertTextfield
        }

        alert.addAction(action)
        alert.addAction(actionCancel)

        present(alert, animated: true, completion: nil)
    }

    @IBAction func editButtonPressed(_ sender: UIBarButtonItem) {

        self.tableView.isEditing = !self.tableView.isEditing
        sender.title = (self.tableView.isEditing) ? "Erledigt" : "Löschen"

    }


    @IBAction func backButtonPressed(_ sender: UIBarButtonItem) {

        self.dismiss(animated: true, completion: nil)
    }


    // MARK: - Data Manipulation

    func saveGift() {
        do {
            try managedContext?.save()

        }catch {
            ("Error saving Gift \(error)")
        }

        self.reloadPerson()
        self.tableView.reloadData()
    }

    func reloadPerson() {
        self.person = self.managedContext?.object(with: self.person!.objectID) as? Person
    }
}

【问题讨论】:

    标签: swift xcode uitableview core-data


    【解决方案1】:

    我相信这是因为删除行操作在您的 managedContext 有机会更新它的计数之前发生。因此,表行数比 managedContext 少 1 个。

    试试这个:

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            managedContext!.delete(giftArray[indexPath.row] as NSManagedObject)
    
            do {
                try managedContext!.save()
                tableView.reloadData() //placing this after managedContext saves and reflects new count.
            } catch {
                ("Error delete gift \(error)")
            }
            self.reloadPerson()
        }
    }
    

    【讨论】:

    • 是的,你是对的。我只需要更改代码顺序。这就是我所做的,它奏效了。 managedContext!.delete(giftArray[indexPath.row] as NSManagedObject) do { try managedContext!.save() } catch { ("Error delete gift (error)") } self.reloadPerson() tableView.deleteRows(at: [indexPath ],带有:.fade) - 非常感谢
    【解决方案2】:

    问题是数据源数组和表视图不同步。

    我的建议是在viewDidLoad 中设置数据源数组一次,并明确处理所有更改。计算变量被非常频繁地调用并且不必要地昂贵。

    好处是您在插入或删除单元格时可以获得漂亮的动画。

    将数据源数组和viewDidLoad替换为

    var giftArray = [Geschenk]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        giftArray = person?.geschenke?.allObjects as? [Geschenk] ?? []
        tableView.reloadData()
    }
    

    tableView(commit: 替换为

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            let objectToDelete = giftArray.remove(at: indexPath.row)
            managedContext!.delete(objectToDelete)
            tableView.deleteRows(at: [indexPath], with: .fade)
            saveGift()
        }
    }
    

    "Hinzufügen" 操作替换为

    let action = UIAlertAction(title: "Hinzufügen", style: .default) { [weak self] (action) in
    
            let newGift = Geschenk(context: self.managedContext!)
            newGift.title = textField.text!
            newGift.isOwned = false
            newGift.owner = self.person
            let insertionIndex = self?.giftArray.count
            self?.giftArray.append(newGift)
            self?.tableView.insertRows(at: IndexPath(row: insertionIndex, section: 0), with: .automatic)
            self?.saveGift()
     }
    

    saveGift 替换为

    func saveGift() {
        do {
            try managedContext?.save()
    
        } catch {
            ("Error saving Gift \(error)")
        }
    }
    

    删除

    罢工>

      func reloadPerson() {
        self.person = self.managedContext?.object(with: self.person!.objectID) as? Person
    }  
    

    对象已正​​确保存到 Core Data 上下文中,但在控制器中独立维护。

    一个强大的替代方案 - 可以非常巧妙地驱动整个 UI - 是带有适当谓词的NSFetchedResultsController

    【讨论】:

      猜你喜欢
      • 2014-08-28
      • 1970-01-01
      • 1970-01-01
      • 2023-02-22
      • 1970-01-01
      • 1970-01-01
      • 2017-12-02
      • 1970-01-01
      • 2023-02-07
      相关资源
      最近更新 更多