使用 NSFetchedResultsController 的核心数据:更改后更新数据
Posted
技术标签:
【中文标题】使用 NSFetchedResultsController 的核心数据:更改后更新数据【英文标题】:Core Data with NSFetchedResultsController: Updating Data After Changes 【发布时间】: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 委托方法中。如果您在这方面发布解决方案,我会接受它作为答案。感谢您的帮助。
【参考方案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 机器,因此无法自己编写测试应用程序。如果您最终弄清楚为什么会发生这种行为,请更新帖子。
没问题。谢谢,山姆。以上是关于使用 NSFetchedResultsController 的核心数据:更改后更新数据的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 3 中难以配置 NSFetchedResultsController