【问题标题】:Core Data with NSFetchedResultsController: Updating Data After Changes使用 NSFetchedResultsController 的核心数据:更改后更新数据
【发布时间】:2018-02-21 06:44:53
【问题描述】:

在过去的 18 个小时左右,我一直在使用 Core Data。我正在使用NSFetchedResultsController 获取数据并使用UITableView 显示数据。添加新记录和删除所选记录不是我的问题。

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    // MARK: - Instance variables
    private let persistentContainer = NSPersistentContainer(name: "Profiles") // core data model file (.xcdatamodeld)
    var managedObjectContext: NSManagedObjectContext?

    // MARK: - IBOutlets
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // loading persistentContainer //
        persistentContainer.loadPersistentStores { (persistentStoreDescription, error) in
            if let error = error {
                print("Unable to Load Persistent Store")
            } else {
                do {
                    try self.fetchedResultsController.performFetch()
                } catch {
                    let fetchError = error as NSError
                    print("\(fetchError), \(fetchError.localizedDescription)")
                }
            }
        }

        // notifications //
        NotificationCenter.default.addObserver(self, selector: #selector(profileDidUpdate), name: NSNotification.Name(rawValue: "HomeViewControllerPictureDidSelect"), object: nil)
    }

    // MARK: - fetchedResultsController(controller with the entity)
    fileprivate lazy var fetchedResultsController: NSFetchedResultsController<Person> = {
        // Create Fetch Request with Entity
        let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()

        // Configure Fetch Request
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "lastName", ascending: true)]

        // Create Fetched Results Controller
        let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)

        // Configure Fetched Results Controller
        fetchedResultsController.delegate = self
        return fetchedResultsController
    }()
    // MARK: - fetchedResultsController


    // MARK: - Notifications
    @objc func profileDidUpdate(notification: NSNotification) {
        let profile = notification.object as! Profile
        let context = persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: "Person", in: context)
        let newPerson = NSManagedObject(entity: entity!, insertInto: context)
        newPerson.setValue(profile.uuid, forKey: "uuid") // uuid is used to make each record unique
        newPerson.setValue(profile.firstName, forKey: "firstName")
        newPerson.setValue(profile.lastName, forKey: "lastName")
        newPerson.setValue(profile.age, forKey: "age")
        newPerson.setValue(profile.pictData, forKey: "pictData")
        do {
            try context.save()
            print("saved...")
        } catch {
            print("failed saving")
        }
    }
    // MARK: - Notifications
}

extension HomeViewController: 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?) {
        switch (type) {
        case .insert:
            if let indexPath = newIndexPath {
                tableView.insertRows(at: [indexPath], with: .fade)
            }
            break;
        case .delete:
            if let indexPath = indexPath {
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
            break;
        default:
            print("...")
        }
    }

    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
    }
}

如上所示,我从另一个视图控制器创建了一条新记录,它将模型(Profile)的对象发送到当前视图控制器(HomeViewController)。感谢NSFetchedResultsController,我不必重新加载表格。

实体有几个属性(年龄、名字、姓氏、pictData、uuid)。我想用两个属性更改列表中的选定记录:名字和姓氏。 uuid 属性用于标识特定记录。

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBAction func editTapped(_ sender: UIButton) {
        guard let indexPath = tableView.indexPathForSelectedRow else {
            return
        }
        let selectedRow = indexPath.row
        if selectedRow >= 0 {
            editRecord(index: selectedRow)
        }
    }

    func editRecord(index: Int) {
        let indexPath = IndexPath(row: index, section: 0)
        let person = fetchedResultsController.object(at: indexPath)
        let uuid = person.uuid!
        let context = self.persistentContainer.viewContext
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
        fetchRequest.predicate = NSPredicate(format: "uuid == %@", uuid)
        do {
            let result = try context.fetch(fetchRequest)
            if (result.count > 0) {
                let managedObject = result[0] as! NSManagedObject
                managedObject.setValue("Donald", forKey: "firstName")
                managedObject.setValue("Washington", forKey: "lastName")
                try context.save()
                print("Changes saved...")
            }
        } catch {
            print("Failed")
        }
    }
}

现在,如果我点击编辑按钮,应用程序不会立即更新列表。如果我重新启动应用程序,我会看到更改。那么,当我对所选记录进行更改时,如何使用NSFetchedResultsController 更新表?谢谢。

【问题讨论】:

  • profileDidUpdate 通知的目的是什么?您为此添加了一个观察者,但该通知是从哪里发送的?
  • @Sam Fischer 我说过它用于从另一个视图控制器添加新记录。
  • 既然您使用的是NSFetchedResultsControllerDelegate,您不应该在您的didChange 方法中处理NSFetchedResultsChangeType.update 的情况吗?现在,您只处理.insert。和.delete 案例。
  • @Sam Fischer 看起来像。我会调查的。谢谢。
  • @Sam Fischer 我已将 case .update 添加到控制器的 didChange 委托方法中。如果您在这方面发布解决方案,我会接受它作为答案。感谢您的帮助。

标签: ios swift uitableview core-data nsfetchedresultscontroller


【解决方案1】:

由于您使用的是NSFetchedResultsControllerDelegate,因此您需要在didChange 方法中处理(针对您的特定用例)NSFetchedResultsChangeType 的以下情况:

  • 插入
  • 删除
  • 更新

您的函数应该如下所示:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
  switch (type) {
  case .insert:
      if let indexPath = newIndexPath {
          tableView.insertRows(at: [indexPath], with: .fade)
      }
      break;
  case .delete:
      if let indexPath = indexPath {
          tableView.deleteRows(at: [indexPath], with: .fade)
      }
      break;
  case .update:
      tableView.reloadRows(at: [indexPath], with: .automatic)
      break;
  default:
      print("...")
  }
}

【讨论】:

  • Sam,即使我进行了更改,该应用程序偶尔也会进入默认状态。你知道为什么吗?谢谢。
  • 嗯,如果NSFetchedResultsChangeType.move,就会发生这种情况。我不知道为什么会这样。你能再描述一下这种行为吗?当您多次单击更新或...时会发生这种情况吗?
  • 我只是输入修改后的值,如 func editRecord() 所示。有时,它会默认。如果我在默认情况下输入 tableView.reloadData(),一切似乎都正常。
  • 如果您的UITableView 中的行数不多,那么我认为这不是问题。我很好奇为什么会出现这种行为,但我现在是一台 Linux 机器,因此无法自己编写测试应用程序。如果您最终弄清楚为什么会发生这种行为,请更新帖子。
  • 没问题。谢谢,山姆。
猜你喜欢
  • 2012-02-17
  • 1970-01-01
  • 2012-06-01
  • 2017-01-13
  • 1970-01-01
  • 2013-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多