2010-11-01 17 views
6

Escribo una pequeña aplicación de iPhone para mi empresa que muestra reservas para cada empleado una semana a la vez. Estoy usando datos básicos para obtener una lista de 'Reservas' para una semana determinada y quiero mostrarlas en una UITableView desglosada en una sección por día de la semana.Agregar secciones adicionales a NSFetchedResultsController

El problema es que necesito mostrar 7 secciones para cada día de la semana (mostrando una celda 'No reservas' donde una sección/fecha no tiene reservas).

Tengo una captura de pantalla de la aplicación tal y como está here (lo siento no puede publicar las imágenes sin embargo, como soy nuevo en StackOverlow)

En este momento estoy lograr esto mediante el uso de un 'fetchResults "método que consiga los reservas y los organiza para una gran variedad de posibles fechas:

- (void)refetchResults {  

// Drop bookings Array, replacing with new empty one 
// 7 slots for 7 days each holding mutable array to recieve bookings where appropraite 
self.bookings = [NSArray arrayWithObjects:[NSMutableArray array], 
        [NSMutableArray array], [NSMutableArray array], 
        [NSMutableArray array], [NSMutableArray array], 
        [NSMutableArray array], [NSMutableArray array], nil]; 

// Create the fetch request for the entity. 
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Booking" inManagedObjectContext:self.managedObjectContext]; 
[fetchRequest setEntity:entity]; 

// Limit to this weeks data 
[fetchRequest setPredicate: 
[NSPredicate predicateWithFormat:@"(date >= %@) && (date <= %@) && (resource == %@)", 
    firstDate,lastDate,resourceId]]; 

// Edit the sort key as appropriate. 
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES]; 
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"recId" ascending:YES]; 
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, sortDescriptor2, nil]; 
[fetchRequest setSortDescriptors:sortDescriptors]; 

// Fetch records in to array 
NSError *error; 
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; 
if (results == nil) { 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
    abort(); 
} 

[fetchRequest release]; 
[sortDescriptor release]; 
[sortDescriptor2 release]; 
[sortDescriptors release]; 

// Walk through records and place in bookings Array as required 
for (Booking *item in results) { 
    // Decide on array index by difference in firstDate and booking date 
    int idx = (int)[[item date] timeIntervalSinceDate:firstDate]/86400; 
    // Add the item to the approp MutArray 
    [(NSMutableArray *)[bookings objectAtIndex:idx] addObject:item]; 
} 

// Reload table 
[tableView reloadData]; 

}

Mi pregunta es: ¿hay alguna manera de conseguir el mismo resultado utilizando NSFetchedResultsController? De alguna manera, necesitaría tener el NSFetchedResultsController para tener 7 secciones, una para cada día de la semana, algunas de ellas posiblemente sin reservas.

Cualquier ayuda muy apreciada :)

Respuesta

2

no he utilizado mucho este hecho, pero es posible que echa un vistazo a protocolo NSFetchedResultsSectionInfo. Se puede usar así, aparentemente:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
NSInteger numberOfRows = 0; 
if ([[fetchedResultsController sections] count] > 0) 
    { 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 
    numberOfRows = [sectionInfo numberOfObjects]; 
    } 
return numberOfRows; 
} 

Buena suerte.

+0

Gracias por la respuesta. A menos que me falta algo, todavía necesitaría hacer un ejercicio de mapeo para encontrar la sección apropiada, es decir: TableView solicitaría la sección 0 a 6 (que representa, por ejemplo, del 1 de noviembre al 7 de noviembre) pero NSFetchedResultsController solo podría tener secciones 0, 1 y 2 (que representan, tal vez, reservas para el 2 de noviembre, el 3 de noviembre y el 6 de noviembre). Podría hacer este mapeo cada vez que necesite referirme a las Reservas o podría guardar en caché la asignación cuando construya NSFetchedResultsController, pero espero que haya una manera mejor/más ordenada de hacerlo. – Paul80nd

+0

Tienes razón. Lamento no ser de más ayuda. Voy a experimentar con esto más tarde, a mí mismo, según lo permita el tiempo. Es curioso saber si/cómo resuelves esto. – westsider

+0

En realidad, pensando un poco más sobre esto, creo que una solución utilizable por el momento es crear un NSFetchedResultsController como de costumbre pero luego, al final de ese conjunto, los campos de una matriz 'mapeo' para traducir las secciones de la tabla vista a las secciones del NSFetchedResultsController. Esto debería significar que puedo obtener las notificaciones de objeto desde el control de resultados (lo cual es bueno;) - Solo necesitaré actualizar la matriz de "mapeo" cuando lleguen las notificaciones para elementos agregados/eliminados del controlador de resultados. De cualquier manera, si encuentro una solución más agradable, me aseguraré de publicarla aquí. – Paul80nd

7

Así que, dado que el clima no es muy agradable afuera, he tenido oportunidad de responder mi propia pregunta y aplicar la 'solución' descrita en mi respuesta a westsider.

La idea es mantener una matriz de "mapeo" (solo una simple matriz de 7 casillas) que mapeará la sección que la tabla preguntará a la sección subyacente de control de resultados recuperados. Cada ranura de matriz tendrá el índice de sección apropiado o '-1' donde no haya secciones subyacentes (y donde se muestre una celda 'Sin reserva').

Por lo tanto, mi método refetchResults se convierte en:

- (void)refetchResults {  

    // Create the fetch request for the entity. 
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Booking" inManagedObjectContext:self.managedObjectContext]; 
    [fetchRequest setEntity:entity]; 

    // Limit to this weeks data 
    [fetchRequest setPredicate: 
    [NSPredicate predicateWithFormat:@"(date >= %@) && (date <= %@) && (resource == %@)", 
     firstDate,lastDate,resourceId]]; 

    // Edit the sort key as appropriate. 
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES]; 
    NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"recId" ascending:YES]; 
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, sortDescriptor2, nil]; 
    [fetchRequest setSortDescriptors:sortDescriptors]; 

    // Set up FRC 
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"date" cacheName:nil]; 
    self.fetchedResultsController = aFetchedResultsController; 
    self.fetchedResultsController.delegate = self; 
    [aFetchedResultsController release]; 
    [fetchRequest release]; 
    [sortDescriptor release]; 
    [sortDescriptor2 release]; 
    [sortDescriptors release]; 

    // Run up FRC 
    NSError *error = nil; 
    if (![fetchedResultsController_ performFetch:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    } 

    // Update FRC map 
    [self updateFRCMap]; 

    // Reload table 
    [tableView reloadData]; 
} 

El mapeo se ajusta en el siguiente método. Esto se invoca cada vez que se necesita actualizar la asignación, por ejemplo, cuando recibo devoluciones de llamada del control de resultados fetched para elementos que se han agregado/eliminado/etc.

- (void)updateFRCMap { 

    // Set mapping table for seven days of week to appropriate section in frc 
    for (int idx=0;idx<7;idx++) { frcMap[idx] = -1; } // Reset mappings 
    // For each section 
    for (int sidx=0; sidx<[[self.fetchedResultsController sections] count]; sidx++) 
    { 
     // If section has items 
     if ([[[self.fetchedResultsController sections] objectAtIndex:sidx] numberOfObjects] > 0) 
     { 
      // Look at first booking of section to get date 
      NSDate *date = [(Booking *)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:sidx]] date]; 
      // Decide on array index by difference in firstDate and booking date 
      int idx = (int)[date timeIntervalSinceDate:firstDate]/86400; 
      // Set map 
      frcMap[idx] = sidx; 
     } 
    } 
} 

Esto probablemente se puede optimizar un poco, pero funciona bien por ahora. Sospecho que podría sufrir problemas de cambio de reloj GMT/BST que necesitarán reparación ... no es que los problemas de cambio de reloj sean tan urgentes, ¿eh Apple? ; P

Después de que es sólo un caso de uso de la matriz de correlación al responder a la tableview:

#pragma mark - 
#pragma mark Table view data source 

// Gets the booking from the fetchedResultsController using a remapped indexPath 
- (Booking *)bookingForMappedIndexPath:(NSIndexPath *)indexPath { 
    return (Booking *)[self.fetchedResultsController objectAtIndexPath: 
         [NSIndexPath indexPathForRow:indexPath.row inSection:frcMap[indexPath.section]]]; 
} 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 
    return 7; // 7 days viewed 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 

    // Rows in section or 1 if no section 
    if (frcMap[section] != -1) { 
     id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:frcMap[section]]; 
     return [sectionInfo numberOfObjects]; 
    } else { 
     return 1; 
    } 

} 

- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 

    static NSString *CellIdentifier = @"RegularCell"; 

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; 
    } 

    // Configure the cell. 
    [self configureCell:cell atIndexPath:indexPath]; 
    return cell; 
} 

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { 

    // If no actual bookings for section then its a blank cell 
    if (frcMap[indexPath.section] == -1) { 

     // Configure a blank cell. 
     cell.textLabel.text = @"No Bookings"; 
     cell.detailTextLabel.text = @""; 

     cell.textLabel.font = [UIFont systemFontOfSize:16]; 
     cell.textLabel.textColor = [UIColor lightGrayColor]; 

     cell.accessoryType = UITableViewCellAccessoryNone; 
     cell.selectionStyle = UITableViewCellSelectionStyleNone; 

    } else { 

     // Regular cell 
     Booking *booking = [self bookingForMappedIndexPath:indexPath]; 
     cell.textLabel.text = booking.desc; 
     cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ %@", booking.location, booking.detail]; 

     cell.textLabel.font = [UIFont systemFontOfSize:14]; 
     cell.textLabel.textColor = [UIColor darkTextColor]; 
     cell.detailTextLabel.font = [UIFont systemFontOfSize:12]; 

     cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 
     cell.selectionStyle = UITableViewCellSelectionStyleBlue; 
    } 
} 

Cualquier comentario o mejores formas de escribir esto son muy bienvenidos :)

+0

Gracias por esto. Tengo exactamente el mismo problema en una aplicación en la que estoy trabajando, intentando mostrar los eventos "Ayer, Hoy, Mañana" con una celda "Nada en este día" en las secciones que están vacías. De hecho fui con la misma solución antes de ver esto. Es malo que FetchedResultsController no tenga ningún soporte para esto ... – Accatyyc

1

que tenía este problema tambiénHe escrito una subclase de NSFetchedResultsController para resolver el problema:

https://github.com/timothyarmes/TAFetchedResultsController

Tim

+0

Aprecie el trabajo arduo de Tim, pero una impresión honesta de su solución es que no se deja caer fácilmente en un proyecto existente. Crear una nueva entidad para soportar esto, y manejar la lógica de eliminación es mucho más trabajo de lo que espero "evitar este pequeño problema" –

Cuestiones relacionadas