如何让 tableView.reloadData() 与核心数据正常工作?

Posted

技术标签:

【中文标题】如何让 tableView.reloadData() 与核心数据正常工作?【英文标题】:How do I get tableView.reloadData() to work properly with core data? 【发布时间】:2019-12-06 04:07:35 【问题描述】:

当我运行应用程序时,我可以填充我的tableview 中的单元格,但是当我保存(从单独的视图控制器)并返回到tableview controller 时,tableView.reloadData() 被调用但没有任何反应。我使用通知中心重新加载数据,然后再弹出。

TableViewController.swift:

lazy var fetchedResultsController: NSFetchedResultsController<Pet> = 
    let fetchRequest = PersistenceManager.shared.fetchRequest()
    let context = PersistenceManager.shared.context
    let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
    return frc as! NSFetchedResultsController<Pet>
()

override func viewDidLoad() 
    super.viewDidLoad()
    navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(pushToAddPetViewController))
    tableView.register(MainTableViewCell.self, forCellReuseIdentifier: "cell")
    do 
        try fetchedResultsController.performFetch()
        tableView.reloadData()
     catch let err 
        print(err)
    


override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)


@objc func loadList(notification: NSNotification)
    DispatchQueue.main.async 
        self.tableView.reloadData()
    

AddPetVC.swift:

func saveContext() 
    // Inputs saved to coreData
    PersistenceManager.shared.saveContext()
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
    self.navigationController?.popViewController(animated: true)

【问题讨论】:

【参考方案1】:

您在 viewDidLoad() 中获取了数据,当您返回视图时不会调用该数据。您应该在 viewWillAppear() 中获取数据并从中重新加载或从通知观察者方法 loadList(notification: NSNotification) 重新加载。因此,在 viewWillAppear() 或 loadList(notification: NSNotification) 中添加以下代码:

 do 
        try fetchedResultsController.performFetch()
        tableView.reloadData()
     catch let err 
        print(err)
    

【讨论】:

【参考方案2】:

当你popViewController回调之前的ViewController时,不会调用viewWillAppear()。

【讨论】:

【参考方案3】:

创建 fetchedResultsController 实例并符合 NSFetchedResultsControllerDelegate。 NSFetchedResultsControllerDelegate 会观察变化并将变化反映在 UI 上

lazy var fetchedResultsController: NSFetchedResultsController<Pet> = 
           let fetchRequest = PersistenceManager.shared.fetchRequest()
           let context = PersistenceManager.shared.context
           let frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
           frc.delegate = self

           return frc as! NSFetchedResultsController<Pet>
    ()

在你看来DidLoad

do 
            try fetchedResultsController.performFetch()
         catch 
            let fetchError = error as NSError
            print("Unable to Save Note")
            print("\(fetchError), \(fetchError.localizedDescription)")
        

现在实现 NSFetchedResultsControllerDelegate

extension YourController: NSFetchedResultsControllerDelegate 

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

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) 
        uiTableView.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 
                uiTableView.insertRows(at: [indexPath], with: .fade)
            
            break;
        case .delete:
            if let indexPath = indexPath 
                uiTableView.deleteRows(at: [indexPath], with: .fade)
            
            break;
        case .update:
            if let indexPath = indexPath, let cell = uiTableView.cellForRow(at: indexPath) as? UserCell 
                configureCell(cell, at: indexPath)
            
            break;
        case .move:
            if let indexPath = indexPath 
                uiTableView.deleteRows(at: [indexPath], with: .fade)
            

            if let newIndexPath = newIndexPath 
                uiTableView.insertRows(at: [newIndexPath], with: .fade)
            
            break;

        
    

实现 UITableViewDataSource & UITableViewDelegate

extension YourController: UITableViewDataSource, UITableViewDelegate 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        guard let sections = fetchedResultsController.sections else 
            return 0
        

        let sectionInfo = sections[section]
        return sectionInfo.numberOfObjects
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "PetCell", for: indexPath) as! Pet
        configureCell(cell, at: indexPath)

        return cell
    

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        let pet = fetchedResultsController.object(at: indexPath)

    

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool 
        return true
    

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) 
        if (editingStyle == .delete) 
            // handle delete (by removing the data from your array and updating the tableview)
            //let user = viewModel.user(for: indexPath.row)
            let pet = fetchedResultsController.object(at: indexPath)
            //Delete pet and reload table
        
    

    func configureCell(_ cell: Pet, at indexPath: IndexPath) 
        let pet = fetchedResultsController.object(at: indexPath)

更多细节和代码请关注演示项目 https://github.com/rheyansh/CoreDataDemo

【讨论】:

以上是关于如何让 tableView.reloadData() 与核心数据正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 UITableView contentOffset 在 tableview reloadData 期间变化很大,而不是保持不变? reloadData 后如何使其保持不变?

未调用 tableView reloadData cellForRowAtIndexPath

[self.tableview reloadData];导致闪烁

使用 tableView.reloadData() 重新加载 TableView 的问题

Swift:tableView.reloadData() 太慢了

使用通知中心和 tableview.reloaddata() 不刷新表视图