25

Lo que quiero hacer es bastante simple. En mi UITableViewController, quiero cargar datos de múltiples NSFetchedResultControllers (tengo varias entidades en mi modelo de datos) y poner los datos de cada uno en una sección diferente en la vista de tabla. Entonces, por ejemplo, todos los ítems recuperados del primer NSFetchedResultController irían en la sección 0 en UITableView, los ítems recuperados del otro entrarían en la sección 1, etc.Datos principales: UITableView con múltiples NSFetchedResultControllers

El proyecto de la plantilla Core Data no muestra cómo hacer esto. Todo (principalmente las rutas de índice) se codifica sin tener en cuenta las secciones (no hay secciones en la plantilla predeterminada) y todo se toma de un único NSFetchedResultController. ¿Hay algún proyecto de ejemplo o documentación que demuestre hacer esto?

Gracias

Respuesta

24

Supongamos por un momento lo siguiente en su cabecera (código de abajo será un poco descuidado, mis disculpas):

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

Vamos a la mesa sabe que quiere tener 2 secciones:

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

Darle los títulos de las secciones:

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

Deje la mesa sabe cuántas filas hay por secciones:

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

    return 0; 
} 

construir el celular:

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

Gracias por la respuesta detallada. Esa parte parece bastante sencilla, sin embargo hay otros dos métodos de los que no estoy tan seguro (lo que hacen y si necesitan algún cambio): http://pastebin.ca/1805761 – indragie

+0

Depende de lo que la aplicación lo hace; es difícil para mí responder sin saber mucho más sobre la aplicación, el diseño, etc. Sin embargo, es probable que tenga una opción segura al hacer que esos métodos envíen a la tabla vista un mensaje de reloadData. – Giao

+0

Gestionado para hacer que esto funcione con un pequeño retoque :-) Gracias – indragie

3

ampliando la solución de Giao con dos NSFetchedResultsControllers - tenemos que recordar nuestra NSFetchedResultsController no sabe acerca nuestras dos secciones y NSIndexPathes devueltos serán siempre para la primera sección.

Así que cuando estamos recibiendo un objeto de configuración de la célula:

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

células Actualización mientras NSFetchedResultsController notó algunos cambios:

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

Buena respuesta, pero después de relanzar la aplicación se debe llamar a 'performFetch'; de lo contrario, el contexto pierde los ManagedObjects obtenidos. ¿Alguna ayuda? – Goppinath

1

múltiple ir a buscar los controladores (y posiblemente múltiples entidades) es un enfoque equivocado . La solución correcta es usar el parámetro sectionNameKeyPath en el NSFetchedResultController para agrupar los resultados en varias secciones. Si piensa en sus entidades de forma diferente, tal vez sean en realidad la misma entidad y, en su lugar, puede usar un itemType de propiedad que luego puede seccionar (y también debe ordenarlo). P.ej. supongamos que tengo entidades de lúpulo y grano, entonces podría cambiarlas por Ingredient y tener un int_16 property ingredientType, que luego tengo una enumeración en el código para almacenar los valores hopType = 0, grainType = 1. Después de todo, el ingrediente es solo un nombre y un peso, que ambos comparten.

Sin embargo, si sus entidades realmente tienen un conjunto distinto de propiedades, la solución correcta es crear una entidad abstracta padre que tenga una propiedad que pueda usar para la sección, p. sortOrder, sectionID o tipo. Cuando luego creas un controlador de búsqueda y recuperas la entidad principal abstracta, obtienes resultados que contienen todas las subentidades. Por ejemplo, en la aplicación de Notes tienen una entidad abstracta NoteContainer que tiene subentidades Cuenta y Carpeta.De esta forma, pueden usar un solo controlador de búsqueda para mostrar la cuenta en la primera celda de la sección y luego tener todas las carpetas en las siguientes celdas. P.ej. Todas las Notas de iCloud (en realidad es la cuenta), luego Notas (es la carpeta predeterminada), seguidas por todas las carpetas personalizadas, luego la carpeta de la papelera. Usan una propiedad sortOrder y la carpeta predeterminada es 1, las carpetas personalizadas son todas 2 y la papelera es 3. Luego, al agregar esto como un descriptor de clasificación, pueden hacer que las celdas se muestren en el orden que desean. Es un poco diferente de su requerimiento porque tienen las 2 entidades mezcladas en diferentes secciones, pero aún puede usarlas solo con diferentes propiedades de clasificación.

La moraleja de la historia es no luchar contra el marco, lo abraza :-)

Cuestiones relacionadas