[英]Invalid update: invalid number of rows in section 0 with NSFetchedResultsController
I have 2 ViewControllers:我有 2 个视图控制器:
VC1 populates its tableView based on CoreData attribute isPicked, which is bool and show only items with true state. VC1 根据 CoreData 属性 isPicked 填充其 tableView,该属性是 bool 并仅显示具有真正 state 的项目。 VC2 is a second Modal (not Full Screen) View Controller which allow user to change the state of isPicked attribute: check and uncheck item (make it true or false). VC2 是第二个模态(非全屏)视图 Controller,它允许用户更改 isPicked 属性的 state:选中和取消选中项目(使其为真或假)。 The idea is user picks needed items end dismiss the VC2 and the items should appear in VC1.这个想法是用户选择需要的项目结束关闭 VC2 并且项目应该出现在 VC1 中。
I have implemented NSFetchedResultsController
to both VC1 and VC2.我已经为 VC1 和 VC2 实现了NSFetchedResultsController
。 And as soon as I press on first item (ie change isPicked state to true) I receive the error from VC1:一旦我按下第一项(即将 isPicked state 更改为 true),我就会收到来自 VC1 的错误:
[error] fault: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) 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, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
Here is how I change a state of item in VC2 (true\false):这是我如何更改 VC2 中项目的 state (真\假):
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let currencyArray = fetchedResultsController.fetchedObjects else { return }
let cell = tableView.cellForRow(at: indexPath) as! PickCurrencyTableViewCell
for currency in currencyArray {
if currency.shortName == cell.shortName.text {
currency.isPicked = !currency.isPicked
coreDataManager.save()
}
}
}
Here is my VC1 NSFetchedResultsController implementation:这是我的 VC1 NSFetchedResultsController 实现:
override func viewDidLoad() {
super.viewDidLoad()
setupFetchedResultsController()
}
func setupFetchedResultsController() {
let predicate = NSPredicate(format: "isForConverter == YES")
fetchedResultsController = createCurrencyFetchedResultsController(and: predicate)
fetchedResultsController.delegate = self
try? fetchedResultsController.performFetch()
}
func createCurrencyFetchedResultsController(with request: NSFetchRequest<Currency> = Currency.fetchRequest(), and predicate: NSPredicate? = nil, sortDescriptor: [NSSortDescriptor] = [NSSortDescriptor(key: "shortName", ascending: true)]) -> NSFetchedResultsController<Currency> {
request.predicate = predicate
request.sortDescriptors = sortDescriptor
return NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
}
Here is my VC1 NSFetchedResultsController delegate:这是我的 VC1 NSFetchedResultsController 委托:
extension VC1: NSFetchedResultsControllerDelegate {
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?) {
if let indexPath = indexPath, let newIndexPath = newIndexPath {
switch type {
case .update:
tableView.reloadRows(at: [indexPath], with: .none)
case .move:
tableView.moveRow(at: indexPath, to: newIndexPath)
case .delete:
tableView.deleteRows(at: [indexPath], with: .none)
case .insert:
tableView.insertRows(at: [indexPath], with: .none)
default:
tableView.reloadData()
}
}
}
}
When I reload the app, picked item shows itself in VC1 (which caused crash).当我重新加载应用程序时,选择的项目会在 VC1 中显示(导致崩溃)。 But every change in VC2 crashes the app again with the same error.但是 VC2 中的每次更改都会再次导致应用程序崩溃,并出现相同的错误。 I don't have any methods to delete items in VC1 or so.我没有任何方法可以删除 VC1 左右的项目。 I need that VC1 tableView just show or hide items according to isPicked state made from VC2.我需要 VC1 tableView 仅根据由 VC2 制成的 isPicked state 显示或隐藏项目。
What I missed in my code?我在代码中遗漏了什么?
UPDATE: my VC1 TableView methods更新:我的 VC1 TableView 方法
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let currency = fetchedResultsController.sections![section]
return currency.numberOfObjects
}
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.shortName.text = currency.shortName
cell.fullName.text = currency.fullName
return cell
}
Doc states:文档状态:
controller(_:didChange:at:for:newIndexPath:)控制器(_:didChange:at:for:newIndexPath:)
indexPath索引路径
The index path of the changed object (this value is nil for insertions).更改后的 object 的索引路径(对于插入,此值为 nil)。
So, you should change the code like this:因此,您应该像这样更改代码:
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 {
tableView.moveRow(at: indexPath, to: newIndexPath)
}
case .delete:
if let indexPath = indexPath {
tableView.deleteRows(at: [indexPath], with: .none)
tableView.reloadData() // << May be needed ?
}
case .insert:
if let newIndexPath = newIndexPath {
tableView.insertRows(at: [newIndexPath], with: .none)
tableView.reloadData() // << May be needed ?
}
default:
tableView.reloadData()
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.