1

Unten ist ein Link zum Download einer vereinfachten Version meiner App, die genau das gleiche Problem hat. Die Plus-Schaltfläche "Hinzufügen" oben fügt einen neuen Datensatz hinzu, der auf Name = 1, Menge = 1 und Abschnitt = 1 festgelegt ist. Durch Auswählen einer Zelle werden sie alle auf die nächste Zahl erhöht. Sie können sehen, dass sowohl der Name als auch die Menge aktualisiert werden, aber der Abschnitt wird nie aktualisiert, bis Sie die App beenden und erneut starten. DropBox Download LinkSection-Update-Ereignis wird nicht von verwandter Entität in swift aufgerufen

Ich habe folgende Beziehung Setup in Coredata:

enter image description here

In meinem Tableviewcontroller, ich meine FetchRequestController (FRC) mit dem folgenden Code erstellen:

func fetchRequest() -> NSFetchRequest { 

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

    return fetchRequest 

} 

func getFCR() -> NSFetchedResultsController { 

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

    return frc 

} 

Wie gezeigt, erstelle ich die Abrufanforderung für die Elementeinheit und sortiere nach Attributen in den Elementen Katalog und Abschnitte. Und speziell auf mein Problem, habe ich die Abschnitte Schlüssel in meinem frc als Abschnitt Attribut in der Abschnitt Entität (die durch die Catalog Entity verwandt ist).

Wenn ich verschiedene Teile der Artikel oder Katalog bin Aktualisierung ich die Tabellenzelle Update korrekt zu sehen (dh das didChangeObject Ereignis genannt wird)

Aber wenn ich den Abschnitt ändern aktualisiert es nie, wenn ich aus vollständig wieder von den Tisch und dann erneut eingeben. (d. h. das didChangeSection-Ereignis wird nie aufgerufen, obwohl sich der Abschnitt ändert)

Unten ist der Code, den ich verwende, um einen bereits vorhandenen Artikeldatensatz zu bearbeiten.

func editItem() { 

    let item: Items = self.item! 

    item.qty = Float(itemQty.text!) 

    item.catalog!.name = Util.trimSpaces(itemName.text!) 
    item.catalog!.brand = Util.trimSpaces(itemBrand.text!) 
    item.catalog!.qty = Float(itemQty.text!) 
    item.catalog!.price = Util.currencyToFloat(itemPrice.text!) 
    item.catalog!.size = Util.trimSpaces(itemSize.text!) 
    item.catalog!.image = UIImageJPEGRepresentation(itemImage.image!, 1) 

    if (self.section != nil) { 
     item.catalog!.sections = self.section 
    } 

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

    if (itemProtocal != nil) { 
     itemProtocal!.finishedEdittingItem(self, item: self.item!) 
    } 

} 

Just zu beachten, wenn ich in einem neuen Datensatz in der Item-Entity, die didChangeObject und didChangeSection Ereignisse sind beide richtig genannt hinzuzufügen. Nur wenn sie bearbeitet werden, wird didChangeSection übersprungen oder verpasst.

Nur zur Vervollständigung, unten ist mein Code, den ich für didChangeObject und didChangeSection verwende.

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.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
     self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
    } 

} 

Als ich dieses Problem gegoogelt, fand ich, dass andere haben ähnliche Probleme wie diese hatten und es scheint ein Merkmal (oder Fehler) des FRC zu sein und wie Xcode Griffe Beziehungen. Im Prinzip beobachtet der Frc nur die Item Entity, und wenn sich die Section Entity ändert, wird sie nicht bei der Frc registriert. Die Leute haben auch verschiedene Hacks vorgeschlagen, aber bisher scheint keiner von ihnen für mich zu arbeiten. Beispiele sind, so etwas zu tun item.catalog.sections = item.catalog.sections Keines der Beispiele hatte den Abschnitt Schlüssel als eine verwandte Einheit, so dass ich nicht sicher bin, ob das ist, warum sie nicht für mich arbeiten.

Also meine Frage ist, ob gibt es eine Möglichkeit, didChangeSection zu sagen, um auszuführen und senden Sie die richtige NSFetchedResultsChangeType? Oder noch besser, gibt es einen Weg, die FRC zu "ermutigen", um zu bemerken, was in der Sektionseinheit geschieht, die durch die Katalogeinheit mit der Item-Entität verbunden ist.

Antwort

2

Nach dieser ein wenig zu spielen, so scheint es, die didChangeSection nur dann, wenn die erste Beziehung in der sectionNameKeyPath genannt abgefeuert wird. In diesem Fall, wenn Sie einen neuen Catalog auf den richtigen Abschnitt verknüpft erstellen, und legen item.catalog = newCatalog). Aber ich denke, das ist zu kompliziert als Workaround.

Eine Lösung wäre, Ihre FRC zu ändern, um die Catalog Objekte anstelle von Items zu holen. Da sie eins-eins zuordnen, sollte die Tabellenansicht die gleiche Struktur beibehalten. Die wichtigsten Änderungen sind:

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 
} 

und

func getFCR() -> NSFetchedResultsController { 

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

    return frc 

} 

dann die Verweise auf frc ändern diese Änderung widerzuspiegeln:

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 
} 

und

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") 
    } 

} 

Weil Sie direkt m die sections Eigenschaft des Objekts catalog odifizierend, löst dies die Methode didChangeSection aus. Das fühlt sich für mich immer noch wie ein bisschen wie ein Hack an, aber da der FRC sich nicht so verhält, wie man es gerne hätte, könnte ein Hack ein notwendiges Übel sein. Diese

+0

Das hat perfekt funktioniert! Danke für den Vorschlag. Ich konnte es auch auf der ursprünglichen App implementieren. Nur eine weitere Änderung, die ich vornehmen musste, bestand darin, 'fetchRequest()' 'ein Prädikat hinzuzufügen, da jeder Artikel einen zugehörigen Katalog hat, aber nicht jedem Katalog ein verwandtes Element zugeordnet ist. 'let pred = NSPredate (format:" items! = nil ")' & 'fetchRequest.predicate = pred' Es besagt, dass ich 6 Stunden warten muss, bevor ich das Bounty vergeben kann, aber ich werde versuchen, es heute Abend zu tun. –

1

Ich habe in einer ähnlichen Situation gelandet und ich endete beim Erstellen eines zweiten NSFetchedResultsController für den anderen Typ, und hörte es nur für Änderungsereignisse und aktualisiert meine Ansicht entsprechend. Keine ideale Lösung, da es einige manuelle Koordination und das Speichern einiger Metadaten erfordert, aber es erledigt die Aufgabe.direkt modifiziert (dh

+0

Das klingt nach einer guten Idee. Wie würdest du beide verwenden? Oder ist es nicht unbedingt notwendig, beide zu verwenden, erstellen Sie sie einfach so, dass der Tabellenansicht-Controller zwei Listener gleichzeitig verwendet? Ich bin mir nicht sicher, ob Sie mir ein schnelles Beispiel oder eine Beschreibung geben können, wie das funktionieren würde. Ich bin noch relativ neu und programmiere. Vielen Dank trotzdem. –

+0

Der Grund, warum ich auf ein Beispiel dafür gehofft hatte, wäre, würde ich annehmen, wenn ich einen zweiten Frc einsetze, würde es den Indexpfad relativ zu sich selbst registrieren, es sei denn, es hätte genau die gleiche Struktur wie die anderen Frc es würde Änderungen relativ zu sich selbst melden und verschiedene Sektionen/Zeilen zum Aktualisieren angeben, als das, was ich in der ersten Version habe. –

+0

Ich habe kein Code-Beispiel für diese Idee.Ich könnte mir vorstellen, dass Sie einen Controller verwenden würden, um die Tabelle tatsächlich zu steuern, und Sie müssten ein wenig arbeiten, um zu verfolgen, welche Abschnitte tatsächlich durch die Daten in der Tabelle dargestellt wurden. Dann würden Sie den anderen Controller verwenden, der die Abschnitte abgerufen hat, um auf Änderungen in den Abschnittsdaten hingewiesen zu werden, die Änderungen mit den Abschnitten, die Sie verfolgten, referenzieren und herausfinden, welche Aktualisierungen Sie in Ihrer Tabelle vornehmen müssen. –

1

ist wirklich eine Erweiterung der Antwort von @CharlesA

Wie Sie die FRC nicht gefunden haben, nichts anderes als seine zugehörige Einheit beobachten für Änderungen. Dies ist keine Funktion oder ein Fehler, es ist ein Implementierungsdetail, da es einen großen Prozentsatz der Verwendung abdeckt und der für die Analyse des Graphen für beliebige Tiefenänderungen erforderliche Code komplex ist. Darüber hinaus können die Callbacks der Delegierten für eine einzelne Änderung, die in den Kontext eingebunden wird, extrem komplex sein und effektiv nicht auf die Tabelle mit Animationen angewendet werden.

Also, Sie müssen wirklich Ihren Ansatz ändern. Die Antwort von Charles, die einen zweiten FRC verwendet, erfordert, dass der zweite FRC verwendet wird, um den ersten FRC anzuweisen, den Abruf erneut auszuführen und die Tabellenansicht vollständig neu zu laden. Sie müssen den Abruf erneut ausführen, da andernfalls die Datenquelle falsch ist und es keine andere Möglichkeit gibt, sie zu aktualisieren. Technisch gesehen könnten Sie versuchen, die Änderungen mit Animation auf die Tabelle anzuwenden, abhängig davon, was Sie wissen/garantieren, wie die Änderungen vorgenommen und in dem Kontext gespeichert werden, der funktionieren könnte. Zum Beispiel, wenn der Benutzer nur einen Abschnitt gleichzeitig ändern kann und automatisch gespeichert wird. Und dass in einem Hintergrund-Thread keine zusammengesetzten Änderungen vorgenommen werden. Die Antwort von @pbasdf ist auch eine geeignete Alternative, aber es wird sich immernoch nur um Beziehungsänderungen handeln, nicht um Änderungen der Werte in den Objekten am Ende dieser Beziehungen - das ist eine Schlüsselunterscheidung, die Sie verstehen und verstehen müssen schätze bei der Verwendung von FRCs.