NSFetchedResultsController error: 'no object at index in section at index 0'

NSFetchedResultsController error: 'no object at index in section at index 0'

我有一个 UITableView,它使用基于 CoreData 属性 isForConverter 的 NSFetchedResultsController 填充它的单元格,该属性是 Bool 并且应该为 true 才能显示。 isForConverter 状态集在另一个 ViewController 中。 当我想从 UITableView 中删除一些单元格并访问未删除的单元格后,我收到错误消息:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'no object at index 5 in section at index 0'

有问题的GIF:https://cln.sh/M1aI9Z

我的删除单元格的代码。我不需要从数据库中删除它,只需将它 isForConverter 从 true 更改为 false:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        let currency = fetchedResultsController.object(at: indexPath)
        currency.isForConverter = false
        coreDataManager.save()
    }
}

NSFetchedResultsController 设置和委托:

func setupFetchedResultsController() {
    let predicate = NSPredicate(format: "isForConverter == YES")
    fetchedResultsController = coreDataManager.createCurrencyFetchedResultsController(with: predicate)
    fetchedResultsController.delegate = self
    try? fetchedResultsController.performFetch()
}

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

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

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
    switch type {
    case .update:
        if let indexPath = indexPath {
            tableView.reloadRows(at: [indexPath], with: .none)
        }
    case .move:
        if let indexPath = indexPath, let newIndexPath = newIndexPath {
            tableView.moveRow(at: indexPath, to: newIndexPath)
        }
    case .delete:
        if let indexPath = indexPath {
            tableView.deleteRows(at: [indexPath], with: .none)
        }
    case .insert:
        if let newIndexPath = newIndexPath {
            tableView.insertRows(at: [newIndexPath], with: .none)
        }
    default:
        tableView.reloadData()
    }
}
}

我注意到如果我只是将 tableView.reloadData() 添加到:

tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)

然后一切正常。但是删除动画真的又快又烦人。另外根据文档,我不应该将 tableView.reloadData() 与 NSFetchedResultsController 一起使用...

如何解决该行为?

更新:

看来我找到了崩溃的原因。这是我的 print() 尝试给出的结果:SCREENSHOT。 什么是 pickedCurrency:这是我创建的自定义类型 Currency 的全局变量,用于接收其属性 currentValue (Double, 87.88)。我只需要从选择的编辑单元格中获取该值。在我使用该值在 cellForRowAt() 进行计算后,计算结果将填充现在不处于编辑模式的所有其他单元格。

我在 textFieldDidBeginEditing() 中定义了 pickedCurrency,因为我收到了我选择编辑的确切货币行:

func textFieldDidBeginEditing(_ textField: UITextField) {
    pickedCurrency = fetchedResultsController.object(at: IndexPath(row: textField.tag, section: 0))
    numberFromTextField = 0
    textField.textColor = UIColor(named: "BlueColor")
    textField.placeholder = "0"
    textField.text = ""
}

然后使用它在 cellForRowAt 中的值根据 pickedCell 值和我放入 activeCell 文本字段中的数字计算所有其他单元格值:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "converterCell", for: indexPath) as! ConverterTableViewCell
    
    let currency = fetchedResultsController.object(at: indexPath)
    cell.flag.image = currencyManager.showCurrencyFlag(currency.shortName ?? "notFound")
    cell.shortName.text = currency.shortName
    cell.fullName.text = currency.fullName
    cell.numberTextField.tag = indexPath.row
    cell.numberTextField.delegate = self
    
    if let number = numberFromTextField, let pickedCurrency = pickedCurrency {
        cell.numberTextField.text = currencyManager.performCalculation(with: number, pickedCurrency, currency)
    }
    return cell
}

似乎当我删除大量单元格然后单击随机单元格进行编辑时它不会更新其 IndexPath(row: textField.tag, section: 0)...

也许有一种方法可以接收我在 cellForRowAt() 中选择进行编辑的货币对象?

我不确定这是否行得通,但作为评论似乎太长了,所以请尝试以下内容。

最初我告诉过您可以使用标签来识别特定视图,这有利于快速简单的实施,但是当像我们现在这样移动/删除行时,将很难像您那样使用标签进行管理不断更新它们。

对于静态 table 视图,它们很好,但如果您的行会发生变化,那么更复杂的模式(如委托/观察者)可能会更好。

无论如何,我认为目前在 textFieldDidBeginEditing 中可以帮助您的情况是停止使用标签来获取索引路径并从被窃听的内容中获取索引路径。

我仍然认为委托模式可能更好,但这可能对你有用:

func textFieldDidBeginEditing(_ textField: UITextField) {
    // Get the coordinates of where we tapped in the table
    let tapLocation = textField.convert(textField.bounds.origin,
                                        to: tableView)
    
    if let indexPath = self.tableView.indexPathForRow(at: tapLocation)
    {
        // don't use tag of textfield anymore
        pickedCurrency
            = fetchedResultsController.object(at: IndexPath(row: indexPath,
                                                            section: 0))
        
        numberFromTextField = 0
        textField.textColor = UIColor(named: "BlueColor")
        textField.placeholder = "0"
        textField.text = ""
    }
}

这有帮助吗?