如何调用 didChangeSection 强制更新 swift 中的部分

How to call didChangeSection to force update of sections in swift

我一直对来自相关实体的 CoreData 和部分有疑问。为了显示我的问题,我制作了尽可能简单的应用程序版本。如果您想下载文件,这是文件的 link。 https://www.dropbox.com/s/y7gcpu7qq2mnrye/Sample%20List%20App.zip?dl=0

这只是一个 table 视图,点击加号按钮会添加一个新项目,其中数量为 1,名称为 1,部分为 1。然后单击单元格将它们全部递增.您可以看到名称和数量都更新得很好,但该部分从未更新。这似乎是因为 frc 没有跟踪部分 Table 中的更改,但目录或项目 table 没有问题。当我退出应用程序然后再次启动它时,这些部分会正确加载。这是我当前的实体和关系(简单应用程序)

下面是来自 tableviewcontroller.

的代码
import UIKit
import CoreData

class TableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

// MARK: - Constants and Variables

let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var frc: NSFetchedResultsController = NSFetchedResultsController()

// MARK: - App loading Functions

override func viewDidLoad() {
    super.viewDidLoad()

    frc = getFCR()
    frc.delegate = self

    do {
        try frc.performFetch()
    } catch {
        print("Failed to perform inital fetch")
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Outlets and Actions

@IBAction func addItemPress(sender: UIBarButtonItem) {

    var entityDesc = NSEntityDescription.entityForName("Items", inManagedObjectContext: self.moc)
    let item = Items(entity: entityDesc!, insertIntoManagedObjectContext: self.moc)

    item.qty = 1

    entityDesc = NSEntityDescription.entityForName("Catalog", inManagedObjectContext: self.moc)
    let catalog = Catalog(entity: entityDesc!, insertIntoManagedObjectContext: self.moc)

    catalog.name = 1

    var section: Sections?
    if (checkSectionName(0, moc: self.moc) == false) {
        entityDesc = NSEntityDescription.entityForName("Sections", inManagedObjectContext: self.moc)
        section = Sections(entity: entityDesc!, insertIntoManagedObjectContext: self.moc)

        section!.section = 1
    } else {
        section = returnSection(0, moc: self.moc)
    }

    item.catalog = catalog
    item.catalog!.sections = section

    do {
        try moc.save()
    } catch {
        fatalError("New item save failed")
    }
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

    if let sections = frc.sections {
        return sections.count
    }

    return 0
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    if let sections = frc.sections {
        let currentSection = sections[section]
        return currentSection.numberOfObjects
    }

    return 0
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
    let item: Items = frc.objectAtIndexPath(indexPath) as! Items

    cell.nameLbl.text = "Item #\(item.catalog!.name!)"
    cell.qtyLbl.text = "Qty: \(item.qty!.stringValue)"

    return cell
}

override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

    if let sections = frc.sections {
        let currentSection = sections[section]
        return "Section \(currentSection.name)"
    }

    return nil
}

func controllerWillChangeContent(controller: NSFetchedResultsController) {

    tableView.beginUpdates()
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {

    tableView.endUpdates()
}

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

    switch type {
    case NSFetchedResultsChangeType.Update:
        self.tableView.reloadSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
    case NSFetchedResultsChangeType.Delete:
        self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
    case NSFetchedResultsChangeType.Insert:
        self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
    case NSFetchedResultsChangeType.Move:
        self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
        self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)
    }
}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

    switch type {
    case NSFetchedResultsChangeType.Update:
        self.tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic)
    case NSFetchedResultsChangeType.Delete:
        self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic)
    case NSFetchedResultsChangeType.Insert:
        self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
    case NSFetchedResultsChangeType.Move:
        self.tableView.moveRowAtIndexPath(indexPath!, toIndexPath: newIndexPath!)
    }
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    let item: Items = frc.objectAtIndexPath(indexPath) as! Items
    var qty: Int = Int(item.qty!)
    qty = qty + 1

    item.qty = qty

    var name: Int = Int(item.catalog!.name!)
    name = name + 1

    item.catalog!.name = name

    var sec: Int = Int(item.catalog!.sections!.section!)
    sec = sec + 1

    var section: Sections?
    if (checkSectionName(sec, moc: self.moc) == false) {
        let entityDesc = NSEntityDescription.entityForName("Sections", inManagedObjectContext: self.moc)
        section = Sections(entity: entityDesc!, insertIntoManagedObjectContext: self.moc)

        section!.section = sec
    } else {
        section = returnSection(sec, moc: self.moc)
    }

    item.catalog!.sections = section

    do {
        try moc.save()
    } catch {
        fatalError("Edit item save failed")
    }

}

func fetchRequest() -> NSFetchRequest {

    let fetchRequest = NSFetchRequest(entityName: "Items")
    let sortDesc1 = NSSortDescriptor(key: "catalog.sections.section", ascending: true)
    let sortDesc2 = NSSortDescriptor(key: "catalog.name", ascending: true)
    fetchRequest.sortDescriptors = [sortDesc1, sortDesc2]

    return fetchRequest

}

func getFCR() -> NSFetchedResultsController {

    frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "catalog.sections.section" , cacheName: nil)

    return frc

}

func checkSectionName(sectionName: NSNumber, moc: NSManagedObjectContext) -> Bool {

    var exists: Bool = false

    let fetchReq = NSFetchRequest(entityName: "Sections")
    let pred = NSPredicate(format: "section == %@", sectionName)
    fetchReq.predicate = pred

    do {
        let check = try moc.executeFetchRequest(fetchReq)
        for rec in check {
            if let name = rec.valueForKey("section") {
                if (name as! NSNumber == sectionName) {
                    exists = true
                }
            }
        }
    } catch {
        fatalError("Failed fetching records when checking if List name already exists")
    }

    return exists

}

func returnSection(sectionName: NSNumber, moc: NSManagedObjectContext) -> Sections {

    let fetchReq = NSFetchRequest(entityName: "Sections")
    let pred = NSPredicate(format: "section == %@", sectionName)
    fetchReq.predicate = pred

    do {
        let check = try moc.executeFetchRequest(fetchReq)
        return check.first! as! Sections
    } catch {
        fatalError("Failed fetching records to return section")
    }
}

}

玩了一会儿之后,似乎 didChangeSection 只有在 sectionNameKeyPath 中命名的第一个关系被直接修改时才会触发(即,在这种情况下,如果您创建一个新的Catalog 链接到正确的部分,并设置 item.catalog = newCatalog)。但我认为这作为解决方法太复杂了。

一个解决方案是更改您的 FRC 以获取 Catalog 个对象而不是 Items。由于它们一对一映射,因此 table 视图应保留相同的结构。主要变化是:

func fetchRequest() -> NSFetchRequest {

    let fetchRequest = NSFetchRequest(entityName: "Catalog")
    let sortDesc1 = NSSortDescriptor(key: "sections.section", ascending: true)
    let sortDesc2 = NSSortDescriptor(key: "name", ascending: true)
    fetchRequest.sortDescriptors = [sortDesc1, sortDesc2]

    return fetchRequest
}

func getFCR() -> NSFetchedResultsController {

    frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "sections.section" , cacheName: nil)

    return frc

}

然后修改对 frc 的引用以反映此更改:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
    let catalog: Catalog = frc.objectAtIndexPath(indexPath) as! Catalog

    cell.nameLbl.text = "Item #\(catalog.name!)"
    cell.qtyLbl.text = "Qty: \(catalog.items.qty!.stringValue)"

    return cell
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    let catalog: Catalog = frc.objectAtIndexPath(indexPath) as! Catalog

    var qty: Int = Int(catalog.items.qty!)
    qty = qty + 1    
    catalog.items.qty = qty

    var name: Int = Int(catalog.name!)
    name = name + 1
    catalog.name = name

    var sec: Int = Int(catalog.sections.section!)
    sec = sec + 1
    var section: Sections?
    if (checkSectionName(sec, moc: self.moc) == false) {
        let entityDesc = NSEntityDescription.entityForName("Sections", inManagedObjectContext: self.moc)
        section = Sections(entity: entityDesc!, insertIntoManagedObjectContext: self.moc)

        section!.section = sec
    } else {
        section = returnSection(sec, moc: self.moc)
    }

    catalog.sections = section

    do {
        try moc.save()
    } catch {
        fatalError("Edit item save failed")
    }

}

因为你是直接修改catalog对象的sections属性,这样会触发didChangeSection方法