使用 CoreData 和 NSFetchedResultsController 删除 UITableView 行

Posted

技术标签:

【中文标题】使用 CoreData 和 NSFetchedResultsController 删除 UITableView 行【英文标题】:Delete UITableView row w/ CoreData and NSFetchedResultsController 【发布时间】:2016-12-03 11:55:41 【问题描述】:

在尝试从 tableView 中删除一行后,应用程序终止并出现以下错误:

Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: '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 (2), plus or minus
the number of rows inserted or deleted from that section (0 inserted,
1 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).'

代码如下:

import UIKit
import CoreData

class PlayerTableViewController: UITableViewController 


lazy var fetchedResultsController:NSFetchedResultsController<TCSpieler> = 
    let fetch:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "TCSpieler")
    let sortByName = NSSortDescriptor(key: "name", ascending: true)
    fetch.sortDescriptors = [sortByName]
    let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let frc = NSFetchedResultsController(fetchRequest: fetch, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
    try! frc.performFetch()
    return frc as! NSFetchedResultsController<TCSpieler>
()

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int 
    return fetchedResultsController.sections?.count ?? 1


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return fetchedResultsController.sections![section].numberOfObjects


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

    let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath) as! PlayerTableViewCell
    // Configure the cell...
    cell.player = fetchedResultsController.object(at: indexPath)

    return cell



// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool 
    // Return false if you do not want the specified item to be editable.
    return true


// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) 
    if editingStyle == .delete 
    let rowToDelete = fetchedResultsController.object(at: indexPath)
    fetchedResultsController.managedObjectContext.delete(rowToDelete)
        try! fetchedResultsController.managedObjectContext.save()

        // Delete the row from the data source
        tableView.deleteRows(at: [indexPath], with: .fade)
     else if editingStyle == .insert 
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    


【问题讨论】:

尝试使用断点并检查您是否正确删除了fetchedResultsController中的数据 你实现NSFetchedResultsController的委托方法了吗? 第 2 部分在删除之前有多少行? 【参考方案1】:

这适用于 swift 4

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) 

        if editingStyle == UITableViewCellEditingStyle.delete 
            let managedObjectContext: NSManagedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext;
            let managedObject: NSManagedObject = fetchedResultsController.object(at: indexPath) as! NSManagedObject;
            managedObjectContext.delete(managedObject);
            do 
                try managedObjectContext.save();
             catch 
                // Error occured while deleting objects
            



         else if editingStyle == UITableViewCellEditingStyle.insert 

        


以下内容还将负责更新 UI 当数据库更新时

extension PlayerTableViewController: NSFetchedResultsControllerDelegate 

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

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) 

        switch type 
        case .insert:
            self. tableView.insertRows(at: [newIndexPath! as IndexPath], with: .fade)
        case .delete:
            self. tableView.deleteRows(at: [indexPath! as IndexPath], with: .fade)
        case .update:
            if(indexPath != nil) 
                let cell = self. tableView.cellForRow(at: indexPath! as IndexPath)
            
        case .move:
            self. tableView.deleteRows(at: [indexPath! as IndexPath], with: .fade)
            self. tableView.insertRows(at: [indexPath! as IndexPath], with: .fade)
        
    

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


【讨论】:

【参考方案2】:

这对我有用:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) 

        if editingStyle == UITableViewCellEditingStyle.delete 
            let managedObjectContext: NSManagedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext;
            let managedObject: NSManagedObject = fetchedResultsController.object(at: indexPath) as! NSManagedObject;
            managedObjectContext.delete(managedObject);
            do 
                try managedObjectContext.save();
                tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.fade);
             catch 
                // Error occured while deleting objects
            



         else if editingStyle == UITableViewCellEditingStyle.insert 

        

    

【讨论】:

以上是关于使用 CoreData 和 NSFetchedResultsController 删除 UITableView 行的主要内容,如果未能解决你的问题,请参考以下文章

Coredata — 入门使用

使用 xmpp 获取 XMPPGroupCoreDataStorageObject,但 NSManagingContextDidSaveChangesNotification 似乎不起作用

CoreData:按创建的顺序获取和排序结果,不使用时间戳

核心数据中的触发器

Coredata 和 Mogenerator,coredata 标志并发问题 EXC_BAD_INSTRUCTION

将 UICollectionView 与 CoreData 和 NSFetchedResultsController 一起使用