25

Was ich tun möchte, ist ziemlich einfach. In meinem UITableViewController möchte ich Daten von mehreren NSFetchedResultControllern laden (ich habe mehrere Entitäten in meinem Datenmodell) und Daten von jedem in einen anderen Abschnitt in der Tabellenansicht legen. So würden beispielsweise alle abgerufenen Elemente aus dem ersten NSFetchedResultController in UITableView in Abschnitt 0 gehen, die abgerufenen Elemente aus dem anderen in Abschnitt 1 usw.Kerndaten: UITableView mit mehreren NSFetchedResultControllern

Das Core Data-Vorlagenprojekt zeigt nicht, wie mach das. Alles (hauptsächlich die Indexpfade) wird ohne Berücksichtigung von Abschnitten codiert (es gibt keine Abschnitte in der Standardvorlage) und alles stammt aus einem einzigen NSFetchedResultController. Gibt es Beispielprojekte oder Dokumentationen, die dies demonstrieren?

Dank

Antwort

24

für einen Moment Angenommen, die folgenden in der Kopfzeile (Code unten etwas nachlässig sein, meine Entschuldigung):

NSFetchedResultsController *fetchedResultsController1; // first section data 
NSFetchedResultsController *fetchedResultsController2; // second section data 

der Tabelle Lassen Sie wissen, dass Sie zwei Abschnitte haben wollen:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 
    return 2; // you wanted 2 sections 
} 

geben sie ihm die Abschnittstitel:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { 
    return [NSArray arrayWithObjects:@"First section title", @"Second section title", nil]; 
} 

der Tabelle Lassen Sie wissen, wie viele Zeilen es pro Abschnitte sind:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    if (section == 0) { 
     return [[fetchedResultsController1 fetchedObjects] count]; 
    } else if (section == 1) { 
     return [[fetchedResultsController2 fetchedObjects] count]; 
    } 

    return 0; 
} 

die Zelle Körperbau:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    ... // table cell dequeue or creation, boilerplate stuff 

    // customize the cell 
    if (indexPath.section == 0) { 
     // get the managed object from fetchedResultsController1 
     // customize the cell based on the data 
    } else if (indexPath.section == 1) { 
     // get the managed object from fetchedResultsController2 
     // customize the cell based on the data 
    } 

    return cell; 
} 
+0

Danke für die ausführliche Antwort. Dieser Teil scheint ziemlich einfach zu sein, aber es gibt zwei andere Methoden, über die ich mir nicht sicher bin (was sie tun und ob sie irgendwelche Änderungen brauchen): http://pastebin.ca/1805761 – indragie

+0

Es hängt irgendwie davon ab, was Sie tun App tut; Es ist schwierig für mich zu antworten, ohne viel mehr über die App, das Design usw. zu wissen. Allerdings haben Sie wahrscheinlich eine sichere Wahl, wenn Sie nur diese Methoden haben, um der Tabelle eine reloadData-Nachricht zu senden. – Giao

+0

geschafft, um das mit ein wenig Basteln zu arbeiten :-) Danke – indragie

3

Giao-Lösung mit zwei NSFetchedResultsControllers erweitern - wir unsere NSFetchedResultsController nicht erinnern müssen wissen, über unsere beiden Abschnitte und zurückgegebenen NSIndexPathes werden immer für den ersten Abschnitt sein.

Also, wenn wir ein Objekt in Zellenkonfiguration erhalten:

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 
    if (!cell) { 
     [tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"]; 
     cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 
    } 
    [self configureCell:cell atIndexPath:indexPath]; 
    return cell; 
} 

-(void)configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath *)indexPath { 
    if (indexPath.section == 0) { 
     NSManagedObject *object = [self.fetchedResults1 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]; 
     //use object to configure cell 
    } else { 
     NSManagedObject *object = [self.fetchedResults2 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]; 
     //use object to configure cell 
    } 
} 

Zellen werden aktualisiert, während NSFetchedResultsController einige Änderungen bemerkt:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 
    NSIndexPath *customIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(controller == self.fetchedResultsController1)?0:1]; 
    NSIndexPath *customNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:(controller == self.fetchedResultsController2)?0:1]; 
    switch(type) { 
     case NSFetchedResultsChangeInsert: 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
     case NSFetchedResultsChangeUpdate: 
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 
     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 
+0

Schöne Antwort, aber nach dem Neustart der App 'performFetch' aufgerufen werden muss, andernfalls verliert der Kontext die abgerufenen ManagedObjects. Irgendeine Hilfe? – Goppinath

1

Mehrere Controller holen (und möglicherweise mehrere Entitäten) ist der falsche Ansatz . Die richtige Lösung besteht darin, den Parameter sectionNameKeyPath param zu NSFetchedResultController zu verwenden, um die Ergebnisse in mehrere Abschnitte zu gruppieren. Wenn Sie anders über Ihre Entitäten nachdenken, sind sie vielleicht die gleiche Entität und stattdessen können Sie eine Eigenschaft itemType verwenden, die Sie dann weiterleiten können (und Sie müssen auch danach sortieren). Z.B. Sagen wir, ich hätte Entitäten Hops und Grains, dann könnte ich diese in Ingredient ändern und habe eine int_16 Eigenschaft ingredientType, die ich dann eine enum im Code habe, um die Werte hopType = 0, grainType = 1 zu speichern. Schließlich ist die Zutat nur ein Name und ein Gewicht, die beide teilen.

Wenn Ihre Entitäten jedoch tatsächlich eine eigene Gruppe von Eigenschaften haben, dann ist die richtige Lösung, eine abstrakte Entität zu erstellen, die über eine Eigenschaft verfügt, die Sie verwenden können, um z. sortOrder, sectionID oder Typ. Wenn Sie dann einen Abruf-Controller erstellen und die abstrakte übergeordnete Entität abrufen, erhalten Sie tatsächlich Ergebnisse, die alle untergeordneten Entitäten enthalten. ZB in der Notes App haben sie eine abstrakte Entität NoteContainer, die Unter-Entitäten Account und Ordner hat.Auf diese Weise können sie einen einzelnen Abrufcontroller verwenden, um das Konto in der ersten Zelle im Abschnitt anzuzeigen, und dann alle Ordner in den folgenden Zellen enthalten. Z.B. Alle iCloud Notes (ist eigentlich das Konto), dann Notes (ist der Standardordner), gefolgt von allen benutzerdefinierten Ordnern, gefolgt vom Papierkorb. Sie verwenden eine sortOrder -Eigenschaft und der Standardordner ist 1, die benutzerdefinierten Ordner sind alle 2 und der Papierkorb ist 3. Dann können sie die Zellen in der von ihnen gewünschten Reihenfolge anzeigen, indem sie dies als Sortierdeskriptor hinzufügen. Es ist ein bisschen anders als Ihre Anforderung, weil sie die zwei Entitäten in verschiedene Abschnitte gemischt haben, aber Sie können es immer noch nur mit anderen Sortiereigenschaften verwenden.

Die Moral der Geschichte ist es, den Rahmen nicht kämpfen, umarmen :-)

Verwandte Themen