25

To, co chcę zrobić, jest całkiem proste. W moim UITableViewController, chcę załadować dane z wielu NSFetchedResultControllers (mam wiele jednostek w moim modelu danych) i umieścić dane z każdego w innej sekcji w widoku tabeli. Na przykład, wszystkie pobrane elementy z pierwszego NSFetchedResultController trafiłyby w sekcji 0 w UITableView, pobrane elementy z drugiej przejść do sekcji 1, itp.Dane podstawowe: UITableView z wieloma NSFetchedResultControllers

Projekt szablonu danych podstawowych nie pokazuje, jak Zrób to. Wszystko (głównie ścieżki indeksu) jest kodowane bez uwzględniania sekcji (nie ma sekcji w domyślnym szablonie) i wszystko jest pobierane z pojedynczego NSFetchedResultController. Czy są jakieś przykładowe projekty lub dokumentacja, które to demonstrują?

Dzięki

Odpowiedz

24

Załóżmy przez chwilę, co następuje w nagłówku (kod poniżej będzie nieco zaniedbany, przepraszam):

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

Niech stół wiedzieć chcesz mieć 2 sekcje:

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

nadać mu tytuły rozdziałów:

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

Niech stół wiedzieć, ile wierszy są na sekcje:

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

    return 0; 
} 

Budowanie komórkę:

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

Dzięki za szczegółową odpowiedź. Ta część wydaje się dość prosta, ale istnieją 2 inne metody, których nie jestem pewien (co robią i czy wymagają zmian): http://pastebin.ca/1805761 – indragie

+0

To zależy od tego, co aplikacja robi; trudno mi odpowiedzieć, nie wiedząc o wiele więcej na temat aplikacji, projektowania itp. Jednak prawdopodobnie masz bezpieczny wybór, po prostu mając te metody, wyślij widok tabeli z komunikatem reloadData. – Giao

+0

Zarządzane, aby to działało z małym majsterkowaniem :-) Dzięki – indragie

3

Rozszerzanie rozwiązania Giao z dwoma NSFetchedResultsControllers - musimy pamiętać, nasza NSFetchedResultsController nie wiem nasze dwie sekcje i zwrócone NSIndexPathes będą zawsze dla pierwszej sekcji.

Więc kiedy jesteśmy coraz obiekt w konfiguracji komórki:

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

Aktualizacja komórki podczas NSFetchedResultsController zauważyłem pewne zmiany:

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

Dobra odpowiedź, ale po ponownym uruchomieniu aplikacji 'performFetch' musi zostać wywołana, w przeciwnym razie kontekst traci pobrane obiekty ManagedObjects. Jakaś pomoc? – Goppinath

1

Multiple pobrać sterowniki (i ewentualnie kilka podmiotów) jest złe podejście . Prawidłowym rozwiązaniem jest użycie parametru sectionNameKeyPath do NSFetchedResultController, aby zgrupować wyniki w wielu sekcjach. Jeśli myślisz o swoich bytach inaczej, być może są one w rzeczywistości tym samym bytem, ​​a zamiast tego możesz użyć właściwości itemType, którą możesz następnie podzielić (i musisz również ją posortować). Na przykład. twierdząc, że miałem obiekty Chmiel i Ziarno, to mogłem je zmienić na Składnik i mieć int_16 właściwość składnikaType, które następnie będę mieć wyliczenie w kodzie do przechowywania wartości hopType = 0, . Po tym wszystkim składnik to tylko nazwa i waga, które obie te części.

Jeśli jednak twoje obiekty rzeczywiście mają odrębny zestaw właściwości, właściwym rozwiązaniem jest utworzenie abstrakcyjnej jednostki nadrzędnej, która ma właściwość, której można użyć do podziału, np. sortOrder, sectionID lub type. Po utworzeniu kontrolera pobierania i pobraniu abstrakcyjnej jednostki nadrzędnej otrzymujesz wyniki zawierające wszystkie podelementy. Np. W aplikacji Notes mają abstrakcyjną jednostkę NoteContainer zawierającą podelementy Konto i folder.W ten sposób mogą używać jednego kontrolera pobierania, aby wyświetlić konto w pierwszej komórce w sekcji, a następnie mieć wszystkie foldery w następujących komórkach. Na przykład. Wszystkie notatki iCloud (w rzeczywistości to konto), następnie Notatki (jest folder domyślny), a następnie wszystkie foldery niestandardowe, a następnie folder kosza. Używają właściwości sortOrder, a domyślny folder to 1, wszystkie foldery niestandardowe to 2, a kosza 3. Następnie, dodając to jako deskryptor sortowania, mogą one wyświetlać komórki w żądanej kolejności. To trochę różni się od twoich wymagań, ponieważ mają one dwie jednostki wymieszane w różnych sekcjach, ale nadal możesz z nich korzystać tylko z różnymi właściwościami sortowania.

Morał z tej historii to nie walcz z frameworkiem, obejmij go :-)

Powiązane problemy