简体   繁体   English

无效更新:使用 NSFetchedResultsController 的第 0 节中的行数无效

[英]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.

相关问题 iOS 8:从NSFetchedResultsController的委托捕获了一个异常,无效的更新:第0节中的行数无效 - iOS 8: An exception was caught from the delegate of NSFetchedResultsController, Invalid update: invalid number of rows in section 0 无效更新:第0部分UITableView中的行数无效 - Invalid update: invalid number of rows in section 0 UITableView NSInternalInconsistencyException(无效更新:第0部分无效的行数) - NSInternalInconsistencyException(Invalid update: invalid number of rows in section 0) 无效的更新:iOS中第0部分的无效行数 - Invalid update: invalid number of rows in section 0 in iOS iOS 5:更新无效:部分中的行数无效 - ios 5: Invalid update: invalid number of rows in section 无效的更新:部分中的行数无效 - Invalid update: invalid number of rows in section &#39;无效更新:第 0 节中的行数无效 - 'Invalid update: invalid number of rows in section 0 无效的更新:部分中的行数无效 - Invalid update: invalid number of rows in section iOS:无效更新:第 0 节中的行数无效 - iOS: Invalid update: invalid number of rows in section 0 NSInternalInconsistencyExceptionException原因:&#39;无效的更新:部分0中的行数无效&#39; - NSInternalInconsistencyException reason: 'Invalid update: invalid number of rows in section 0'
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM