NSFetchedResultsController 没有正确更新 tableView

Posted

技术标签:

【中文标题】NSFetchedResultsController 没有正确更新 tableView【英文标题】:NSFetchedResultsController not properly updating tableView 【发布时间】:2018-08-02 19:49:03 【问题描述】:

我在 viewDidLoad 中获取一些项目 (NSManagedObject),并且我正在使用 NSFetchedResultsController 代表来更新 swift ios 中的 tableView 单元格(来自 Apple 文档的代表用于插入、删除等),问题是当我将一个项目添加到相同 ManagedObject 类型的核心数据中,我希望表会使用新项目进行更新,但最后一个项目会从 tableview 中删除,并且不会添加新项目。当我再次加载屏幕时,我只看到新项目......可能出了什么问题? 这是我的 fetchedResultsController:

lazy var fetchedResultsController: NSFetchedResultsController<Message> = 
    let context = CoreDataManager.shared.persistentContainer.viewContext
    let request: NSFetchRequest<Message> = Message.fetchRequest()

    request.sortDescriptors = [
        NSSortDescriptor(key: "createdAt", ascending: true)
    ]
    request.fetchLimit = 40

    let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)

    // fetch only messages with that recipient id of the chat.
    let recipientId = "\(chat!.recipientId)"
    request.predicate = NSPredicate(format: "recipientId == %@", recipientId)

    return frc
()

这就是我创建和添加新消息的方式

func createNewTextMessage(recipientId: String, content: String) 

    ...

    guard let id = Int32(recipientId) else  return 
    guard let chat = fetchSingleChat(recipientId: id) else  return 

    let context = persistentContainer.viewContext
    let message = Message(context: context)

    let date = Date()

    message.chatId = chat.id
    message.createdAt = date
    message.isRead = false
    message.senderId = Int32(currentUserId)
    message.senderName = currentUserName
    message.senderPhoneNumber = currentUserPhoneNumber
    message.status = MessageStatus.unsent.rawValue
    message.type = MessageType.text.rawValue
    message.owner = MessageOwnerType.mine.rawValue
    message.content = content
    message.recipientId = chat.recipientId
    message.recipientName = chat.title

    message.messageId = "\(currentUserToken)_\(date.millisecondsSince1970)"

    // here's where i'm setting some relationship (chat to message: 1-to-many)
    message.chat = chat 
    chat.lastMessage = message
    message.chat?.lastMessage = message

    do 
        try context.save()
     catch let err 
        print("failed to save private chat:", err)
    


聊天 ManagedObject 有一个 lastMessage 属性,它是消息类型(也是一个托管对象),我将新创建的消息设置为聊天的最后一条消息。问题可能来自那里。 这里也是委托代码:

// MARK: - NSFetchedResultsController Delegates

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


func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) 
    switch type 
    case .insert:
        tableView.insertSections(IndexSet(integer: sectionIndex), with: .fade)
    case .delete:
        tableView.deleteSections(IndexSet(integer: sectionIndex), with: .fade)
    case .move:
        break
    case .update:
        break
    


func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) 
    switch type 
    case .insert:
        tableView.insertRows(at: [newIndexPath!], with: .right)
    case .delete:
        tableView.deleteRows(at: [indexPath!], with: .fade)
    case .update:
        tableView.reloadRows(at: [indexPath!], with: .fade)
    case .move:
        tableView.moveRow(at: indexPath!, to: newIndexPath!)
    


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

【问题讨论】:

...我正在使用 NSFetchedResultsController 代表。你在哪里设置委托? 委托在 viewDidLoad 中设置,这也是我进行获取的地方 也许您也应该向我们展示委托代码。 我已添加委托代码 【参考方案1】:

请注意,您对移动行 (.move) 的处理仅移动一行,您应该实现它以移动多行。不确定这是否能解决您的问题,这取决于您的数据是如何排序的。

这是我实现 .move 的方式

case .move:
    if let indexPath = indexPath 
        tableView.removeRows(at: IndexSet(integer: indexPath.item), withAnimation: .effectFade)
    
    if let newIndexPath = newIndexPath 
        tableView.insertRows(at: IndexSet.init(integer: newIndexPath.item), withAnimation: .effectFade)
    

【讨论】:

我的排序是基于日期的。除了排序会触发移动吗?因为我只是插入新项目 就像我说的,我不确定它是否能解决您的问题,也许您可​​以尝试一下,但它仍然是一个改进,因为它可以像您的其他 switch 案例一样处理多行。如果没有帮助,我会删除这个答案。

以上是关于NSFetchedResultsController 没有正确更新 tableView的主要内容,如果未能解决你的问题,请参考以下文章

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