NSFetchedResultsController 在错误的 indexPath 中添加项目

Posted

技术标签:

【中文标题】NSFetchedResultsController 在错误的 indexPath 中添加项目【英文标题】:NSFetchedResultsController adds item in wrong indexPath 【发布时间】:2017-06-13 16:23:44 【问题描述】:

我编写了简单的计数器应用程序。它使用 CoreData 来存储数据。在模型中,我有一个名为“Counter”的实体,其属性为“value”(Int64)和“position”(Int16)。第二个属性是 tableView 中的位置。用户可以添加、删除或增加计数器。我还使用 NSFetchedResultsController 来填充我的 tableView。应用程序可以工作,但是当我添加 5 个计数器、更改它们的值、删除第二个和第三个值并添加新的值时,计数器将不会插入到 tableView 的末尾(这里是视频https://youtu.be/XXyuEaobd3c)。我的代码有什么问题?

import UIKit
import CoreData

class TableViewController: UITableViewController, NSFetchedResultsControllerDelegate 

let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

var frc: NSFetchedResultsController<Counter> = 
    let fetchRequest: NSFetchRequest<Counter> = Counter.fetchRequest()
    let sortDescriptor = NSSortDescriptor(key: "position", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
    do 
        try frc.performFetch()
     catch 
        print(error)
    
    return frc
()


@IBAction func addCounter(_ sender: UIBarButtonItem) 
    let counter = Counter(context: context)
    counter.value = 0
    counter.position = Int16(frc.fetchedObjects!.count)
    do 
        try context.save()
     catch 
        print(error)
    



@IBAction func longPressed(_ sender: UILongPressGestureRecognizer) 
    if sender.state == UIGestureRecognizerState.began 
        let touchPoint = sender.location(in: self.view)
        if let indexPath = tableView.indexPathForRow(at: touchPoint) 
            // your code here, get the row for the indexPath or do whatever you want
            let counter = frc.object(at: indexPath)
            counter.value = 0

            do 
                try context.save()
             catch 
                print(error)
            
        
    




override func viewDidLoad() 
    super.viewDidLoad()

    frc.delegate = self



// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int 
    guard let sections = frc.sections?.count else return 0
    return sections


override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    guard let sections = frc.sections else return 0
    return sections[section].numberOfObjects



override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    let counter = frc.object(at: indexPath)
    cell.textLabel!.text! = String(counter.value)

    return cell


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


func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) 
    switch type 
    case .insert:
        guard let indexPath = newIndexPath else return
        tableView.insertRows(at: [indexPath], with: .fade)
    case .delete:
        guard let indexPath = indexPath else return
        tableView.deleteRows(at: [indexPath], with: .fade)
    case .update:
        guard let indexPath = indexPath else return
        let cell = tableView.cellForRow(at: indexPath)
        let counter = frc.object(at: indexPath)
        cell?.textLabel?.text = String(counter.value)
    default:
        break
    


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


override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
    let counter = frc.object(at: indexPath)
    counter.value += Int64(1)

    do try context.save()
    catch print(error)

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


   // Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) 
    if editingStyle == .delete 
            let counter = frc.object(at: indexPath)
            context.delete(counter)

        do 
            try context.save()
         catch 
            print(error)
        
    

【问题讨论】:

【参考方案1】:

您以错误的方式添加职位。 假设你有

A1、A2、A3、A4、A5

然后删除A2、A3

在那之后你有 A1、A4、A5

所以添加新的是 B3,因为现在计数是 3 A1、B3、A4、A5

添加另一行变为 B4,因此它可以在 A4 之前或之后的任何位置。

这个顺序是由于位置上的排序顺序。

您可以通过获取最后获取的对象并增加位置和结果来解决此问题:

A1、A4、A5、B6、B7

【讨论】:

以上是关于NSFetchedResultsController 在错误的 indexPath 中添加项目的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?