IOS13+ NSPersistentCloudKitContainer UIRefreshControl如何刷新UITableView内容

IOS13+ NSPersistentCloudKitContainer UIRefreshControl how to refresh UITableView content

我正在使用 NSPersistentCloudKitContainer 同步数据。使用新的 iCloud 内容刷新 UITableView 内容的正确代码是什么?如果我关闭 UITableView 并重新打开它,新内容会立即出现。

我正在使用标准代码刷新 UITableView,但使用该代码我无法从 iCloud 获取新数据?

class listFirmenTableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        firmenTableView.refreshControl = UIRefreshControl()
        firmenTableView.refreshControl?.addTarget(self, action:
                                           #selector(handleRefreshControl),
                                           for: .valueChanged)
    }


@objc func handleRefreshControl() {
    reload(tableView: self.tableView)

   // Dismiss the refresh control.
   DispatchQueue.main.async {
      self.firmenTableView.refreshControl?.endRefreshing()
   }
}


func reload(tableView: UITableView) {

    tableView.reloadData()
    tableView.layoutIfNeeded()

}

使用 NSFetchedResultsController 将使您的 table 自动与 CoreData 数据库保持同步。在下面的代码中,您必须将 YourEntityName 的实例替换为您在项目中使用的真实数据库实体名称。

您可能还想适当地设置 request.sortDescriptors。例如,如果您使用的是搜索栏,则可以在排序描述符中包含搜索栏文本,然后每次搜索栏文本更改时都必须调用方法 updateFetchedResultsController()

至于UIRefreshControl,此后甚至不需要。

import UIKit
import CoreData

class YourTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

    var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer

    var fetchedResultsController: NSFetchedResultsController<YourEntityName>? {
        didSet {
            fetchedResultsController?.delegate = self
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        updateFetchedResultsController()
    }

    func updateFetchedResultsController() {
        guard let context = container?.viewContext else {
            return
        }

        context.performAndWait {
            let request: NSFetchRequest<YourEntityName> = YourEntityName.fetchRequest()
            request.sortDescriptors = nil // set sort descriptors if you want data ordered specifically
            request.predicate = nil // set predicate if you only want specific data

            fetchedResultsController = NSFetchedResultsController<YourEntityName>(
                fetchRequest: request,
                managedObjectContext: context,
                sectionNameKeyPath: nil,
                cacheName: nil
            )

            do {
                try fetchedResultsController!.performFetch()
                tableView.reloadData()
            } catch {
                // Handle the error, for example, present an alert
            }
        }
    }

    // MARK:- UITableViewDataSource

    override func numberOfSections(in tableView: UITableView) -> Int {
        return fetchedResultsController?.sections?.count ?? 0
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let sections = fetchedResultsController?.sections, sections.count > 0 {
            return sections[section].numberOfObjects
        }
        return 0
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        if let sections = fetchedResultsController?.sections, sections.count > 0 {
            return sections[section].name
        }
        return nil
    }

    override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return fetchedResultsController?.sectionIndexTitles
    }

    override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
        return fetchedResultsController?.section(forSectionIndexTitle: title, at: index) ?? 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellIdentifier", for: indexPath) as! YourTableViewCell        
        let object = fetchedResultsController?.object(at: indexPath)
        // use data in object to update your cell
        return cell
    }

    // MARK:- NSFetchedResultsControllerDelegate

    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([sectionIndex], with: .fade)
        case .delete: tableView.deleteSections([sectionIndex], with: .fade)
        default: 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: .fade)
        case .delete:
            tableView.deleteRows(at: [indexPath!], with: .fade)
        case .update:
            tableView.reloadRows(at: [indexPath!], with: .fade)
        case .move:
            tableView.deleteRows(at: [indexPath!], with: .fade)
            tableView.insertRows(at: [newIndexPath!], with: .fade)
        default:
            break
        }
    }

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

此外,请确保在 AppDelegate 中设置 container.viewContext.automaticallyMergesChangesFromParent = true,如下所示:

lazy var persistentContainer: NSPersistentCloudKitContainer = {

    let container = NSPersistentCloudKitContainer(name: "YourEntityName")

    container.loadPersistentStores { description, error in
        if let error = error {
            // handle the error
        }
    }

    container.viewContext.automaticallyMergesChangesFromParent = true
    return container
}()