25

Quello che voglio fare è piuttosto semplice. Nel mio UITableViewController, voglio caricare i dati da più NSFetchedResultControllers (ho più entità nel mio modello di dati) e inserire i dati da ciascuno in una sezione diversa nella vista tabella. Quindi, ad esempio, tutti gli elementi recuperati dal primo NSFetchedResultController entrano nella sezione 0 di UITableView, gli elementi recuperati dall'altra parte nella sezione 1, ecc.Dati principali: UITableView con più NSFetchedResultControllers

Il progetto di modello Dati principali non mostra come Fai questo. Tutto (principalmente i percorsi dell'indice) è codificato senza tenere conto delle sezioni (non ci sono sezioni nel modello predefinito) e tutto è preso da un singolo NSFetchedResultController. Ci sono progetti di esempio o documentazione che dimostrano di fare questo?

Grazie

risposta

24

Supponiamo per un momento i seguenti nell'intestazione (codice qui sotto sarà leggermente sciatta, le mie scuse):

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

Let tabella sa che si desidera avere 2 sezioni:

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

Dategli i titoli delle sezioni:

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

Lasciate che la tabella di sapere quante righe ci sono per sezioni:

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

    return 0; 
} 

costruire il cellulare:

- (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

Grazie per la risposta dettagliata. Quella parte sembra abbastanza semplice, tuttavia ci sono altri 2 metodi di cui non sono così sicuro (cosa fanno e se hanno bisogno di modifiche): http://pastebin.ca/1805761 – indragie

+0

Dipende da cosa è il tuo l'app fa; è difficile per me rispondere senza sapere molto di più sull'app, sul design, ecc. Tuttavia, probabilmente hai una scelta sicura solo quando quei metodi inviano la visualizzazione tabella a un messaggio reloadData. – Giao

+0

Gestito per ottenere questo funziona con un po 'armeggiare :-) Grazie – indragie

3

Estendendo la soluzione di Giao con due NSFetchedResultsControllers - dobbiamo ricordare la nostra NSFetchedResultsController non conoscono le nostre due sezioni e restituite NSIndexPathes saranno sempre per la prima sezione.

Così, quando stiamo ottenendo un oggetto in configurazione della cella:

- (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 
    } 
} 

Aggiornamento cellule, mentre NSFetchedResultsController notato alcuni cambiamenti:

- (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

Bella risposta, ma dopo aver riavviato l'app 'performFetch' deve essere chiamato altrimenti il ​​contesto perde i ManagedObjects recuperati. Qualsiasi aiuto? – Goppinath

1

multipla prendere controllori (ed entità possibilmente più) è l'approccio sbagliato . La soluzione corretta è utilizzare il parametro sectionNameKeyPath per il NSFetchedResultController per raggruppare i risultati in più sezioni. Se pensi alle tue entità in modo diverso, forse sono in realtà la stessa entità e invece puoi usare un itemType di proprietà su cui puoi fare una sezione (e devi anche ordinare su di esse). Per esempio. dire che avevo entità Hops and Grains quindi potevo cambiarle in Ingredient e avere un ingrediente di proprietà int_16Type che poi ho un enum in codice per memorizzare i valori hopType = 0, grainType = 1. Dopo tutto l'ingrediente è solo un nome e un peso, che entrambi condividono.

Se tuttavia le entità dispongono di un insieme distinto di proprietà, la soluzione corretta consiste nel creare un'entità astratta padre con una proprietà che è possibile utilizzare per la sezione, ad es. sortOrder, sectionID o type. Quando si crea un controller di recupero e si recupera l'entità padre astratta, si ottengono effettivamente risultati contenenti tutte le entità secondarie. E.g nell'app Note hanno un'entità astratta NoteContainer che ha Account e Cartelle sub-entità.In questo modo possono utilizzare un singolo controller di recupero per visualizzare l'account nella prima cella della sezione e quindi disporre di tutte le cartelle nelle celle seguenti. Per esempio. Tutte le note di iCloud (in realtà è l'account), quindi Notes (è la cartella predefinita), seguita da tutte le cartelle personalizzate, quindi dalla cartella cestino. Utilizzano una proprietà sortOrder e la cartella predefinita è 1, le cartelle personalizzate sono tutte 2 e il cestino è 3. Quindi aggiungendo questo come descrittore di ordinamento, le celle possono essere visualizzate nell'ordine desiderato. È un po 'diverso dal tuo requisito perché hanno le 2 entità mescolate in diverse sezioni, ma puoi ancora usarlo solo con diverse proprietà di ordinamento.

La morale della storia è non combattere il quadro, abbracciarlo :-)

Problemi correlati