在 Swift 中使用 Core Data 在表格视图的部分中显示数据

Posted

技术标签:

【中文标题】在 Swift 中使用 Core Data 在表格视图的部分中显示数据【英文标题】:Displaying data in sections in a Table View using Core Data in Swift 【发布时间】:2015-11-11 14:50:31 【问题描述】:

我在使用 Swift 中的 Core Data 在表格视图中显示按部分分组的数据时遇到了一些麻烦。

我将用一个例子来说明这个问题。让我们假设三个类:订单、产品和服务。类 Order 具有属性“日期”以及与 Product 和 Service 的一对多关系。

对于每个订单,应创建一个部分以显示在特定日期订购的所有产品和服务。

使用 NSFetchedResultsController 根据属性日期获取所有订单。这提供了正确数量的部分。 但是,由于“numberOfRowsInSection”是基于订单数量而不是产品和服务数量,因此在每个部分的单独行中显示所有产品和服务时遇到问题。

下面是说明我的问题的代码。希望任何人都可以帮助我走向正确的方向。

提前致谢,

杰拉德

import UIKit
import CoreData

class ExampleViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate 

@IBOutlet weak var tableView: UITableView!

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController()

var orders = [Order]()

override func viewDidLoad() 
    super.viewDidLoad()
    fetchedResultController.delegate = self
    tableView.dataSource = self
    tableView.delegate = self

    fetchData()


func numberOfSectionsInTableView(tableView: UITableView) -> Int 
    return fetchedResultController.sections?.count ?? 0


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    let sectionInfo = fetchedResultController.sections![section] as NSFetchedResultsSectionInfo
    return sectionInfo.numberOfObjects
    // Unfortunatelly, this provides not the total number of products and services per section


func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
    let textCellIdentifier = "ExampleTableViewCell"
    let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! ExampleTableViewCell

    let order = orders[indexPath.row] // Data fetched using NSFetchedResultsController

    let products = order.products!.allObjects as! [Product] // Swift Array
    let services = order.services!.allObjects as! [Service] // Swift Array

    // Each product and each service should be displayed in separate rows. 
    // Instead, all products and services are displayed in a single row as below
    var displaytext = ""
    for product in products 
        displaytext += product.name! + "\n"
    

    for service in services 
        displaytext += service.name! + "\n"
    

    cell.orderLabel.text = displaytext // display text in ExampleTableViewCell

    return cell


func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    if let sections = fetchedResultController.sections 
        let currentSection = sections[section]
        return currentSection.name
    
    return nil



func orderFetchRequest() -> NSFetchRequest 
    let fetchRequest = NSFetchRequest(entityName: "Order")
    let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
    let predicate = NSPredicate(format: "date >= %@ AND date <= %@", startDate, endDate) // startDate and endData are defined elsewhere

    fetchRequest.sortDescriptors = [sortDescriptor]
    fetchRequest.predicate = predicate

    return fetchRequest


func fetchData() 
    let fetchRequest = orderFetchRequest()
    fetchedResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: "date", cacheName: nil)

    do 
        orders = try managedObjectContext.executeFetchRequest(fetchRequest) as! [Order]
    
    catch let error as NSError 
        print("Could not fetch \(error), \(error.userInfo)")
    

    do 
        try fetchedResultController.performFetch()
    
    catch _ 
    

【问题讨论】:

所以您希望每个订单有一个部分,并且在每个部分中为所有产品行,然后为所有服务行? FRC 不会轻易帮助您。它想要显示订单并深入了解产品和服务...... 这正是我想要的。实际上,我希望可以获取父类 Order 的数据(使用 NSSortDescriptor 和 NSPredicate),并根据子类 Product 和 Service 的实例数提供 Table View 每个部分的行数。当 NSFetchedResultsController 无法(轻松)实现这一点时,是否有另一种方法来执行此操作? 【参考方案1】:

正如韦恩所说,FRC 并没有多大帮助。 FRC 有两个主要优点,除了普通的 fetch 之外:它可以为您管理部分,并且可以通过其委托方法自动处理插入/删除/更新。第一个在您的情况下没有用,因为您希望每个订单有一个部分。第二个没有什么帮助,因为 FRC 只会监视一个实体的变化,而您的表格视图是由三个构建的。

不过,我认为您可以使用 FRC 来达到某种效果。首先,不要理会sectionNameKeyPath:您在Orders 和table view 部分之间有一个一对一的映射,因此您可以使用tableview 的section 作为FRC 的fetchedObjects 上的索引来识别每个部分的顺序部分。然后可以通过将相关Orderproductsservices 的计数相加来找到numberOrRowsInSection。混乱(并且可能很慢)位是将表格视图的行映射到productsservices 的正确元素。

import UIKit
import CoreData

class ExampleViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate 

    @IBOutlet weak var tableView: UITableView!

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
    var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController()

    var orders = [Order]()
    var startDate : NSDate = NSDate()
    var endDate : NSDate = NSDate()

    override func viewDidLoad() 
        super.viewDidLoad()
        fetchedResultController.delegate = self
        tableView.dataSource = self
        tableView.delegate = self

        fetchData()
    

    func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return fetchedResultController.fetchedObjects!.count
    

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        let order = fetchedResultController.fetchedObjects![section] as! Order
        return ((order.products?.count ?? 0) + (order.services?.count ?? 0))
    

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let textCellIdentifier = "ExampleTableViewCell"
        let row = indexPath.row

        let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! ExampleTableViewCell

        let order = fetchedResultController.fetchedObjects![indexPath.section] as! Order // Data fetched using NSFetchedResultsController
        let products = (order.products?.allObjects ?? [Product]()) as! [Product] // Swift Array
        let services = (order.services?.allObjects ?? [Service]()) as! [Service] // Swift Array

        if (row < products.count)  // this is a Product row
            cell.orderLabel.text = products[row].name!
         else  // this is a Service row
            cell.orderLabel.text = services[row-products.count].name!
        
        return cell
    

    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        let order = fetchedResultController.fetchedObjects![section] as! Order
        return "\(order.date)"
    


    func orderFetchRequest() -> NSFetchRequest 
        let fetchRequest = NSFetchRequest(entityName: "Order")
        let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
        let predicate = NSPredicate(format: "date >= %@ AND date <= %@", startDate, endDate) // startDate and endData are defined elsewhere

        fetchRequest.sortDescriptors = [sortDescriptor]
        fetchRequest.predicate = predicate

        return fetchRequest
    

    func fetchData() 
        let fetchRequest = orderFetchRequest()
        fetchedResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath:nil, cacheName: nil)

        do 
            try fetchedResultController.performFetch()
        
        catch let error as NSError 
            print("Could not fetch \(error), \(error.userInfo)")
        
    

可能有更简洁的编码方式;我不是 Swift 爱好者。

如果您使用 FRC 委托方法,您至少可以观看新的/删除的订单,并根据需要向电视添加/删除部分,并且您可以使用更新重新加载相关部分。

【讨论】:

非常感谢您的帮助。遗憾的是,使用 FRC 只能监视一个实体的更改,但您提出的解决方法完全符合我的要求!

以上是关于在 Swift 中使用 Core Data 在表格视图的部分中显示数据的主要内容,如果未能解决你的问题,请参考以下文章

可以在 Swift Playground 中使用 Core Data 吗?

如何使用 Swift 在 Core Data 中组合属性?

如何在 Core Data 中使用 swift 4 Codable?

如何在 Core Data 中使用 swift 4 Codable?

如何在 Core Data 中使用 swift 4 Codable?

使用 Swift 和 XCode 在 Core Data 中存储整数