Después de días de investigación y re-codificación estoy bastante perplejo. Mi objetivo es hacer que se ejecute una aplicación de prueba con una única tabla vista poblada de dos fetchedResultControllers separados.TableView con dos instancias de NSFetchedResultsController
Tengo una serie de artículos en una lista de compras, cada uno con un departamento y una bandera booleana 'recogida'. Los artículos no recogidos deben enumerarse por departamento, seguidos de una sola sección que contiene todos los artículos recolectados (independientemente del departamento). A medida que un usuario marque los elementos no cobrados, se moverá a la sección "recolectada". Si él/ella elimina la marca de un artículo recolectado, debe regresar a su departamento correcto.
Para lograr la primera parte (artículos no cobrados), puedo tener un fetchedResultsController sencilla que recupera todos los elementos en los que recogen = NO, y se seccionaron los resultados por departamento:
- (NSFetchedResultsController *)firstFRC {
// Set up the fetched results controller if needed.
if (firstFRC == nil) {
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// fetch items
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// only if uncollected
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(collected = NO)"];
[fetchRequest setPredicate:predicate];
// sort by name
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// fetch results, sectioned by department
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"department" cacheName:nil];
aFetchedResultsController.delegate = self;
self.firstFRC = aFetchedResultsController;
}
return firstFRC;
}
yo pongo el número de filas, secciones y encabezados de sección como sigue:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return firstFRC.sections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[firstFRC.sections objectAtIndex:section] numberOfObjects];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[[firstFRC sections] objectAtIndex:section] name];
}
también utilizo el controllerWillChangeContent repetitivo, didCha ngeObject y controllerDidChangeContent para agregar/eliminar celdas a medida que cambian los FRC.
Por razones de brevedad, no incluirá el código para la visualización de la célula, pero esencialmente tirar el producto correcto basado en ruta del índice de la célula, establecer el texto/subtítulo de la célula, y adjuntar una de las dos imágenes de marca de verificación, dependiendo de si el artículo es recolectado o no. También conecto esta imagen (que está en un botón) para alternar entre marcada y no marcada cuando se toca, y actualizo el ítem en consecuencia.
Esta parte funciona bien. Puedo ver mi lista de artículos por departamento, y cuando marco uno como recogido, lo veo abandonar la lista como se esperaba.
Ahora intenté agregar la sección inferior, que contiene todos los elementos recopilados (en una sola sección). Primero configuré un segundo fetchedResultsConroller, esta vez para buscar solo artículos no recogidos y sin seccionar. También tuve que actualizar la siguiente:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections - add one for 'collected' section
return firstFRC.sections.count + 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section
if (section < firstFRC.sections.count) {
return [[firstFRC.sections objectAtIndex:section] numberOfObjects];
}
else {
return secondFRC.fetchedObjects.count;
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section < firstFRC.sections.count) {
return [[[firstFRC sections] objectAtIndex:section] name];
}
else{
return @"Collected";
}
}
entonces he actualizado el cellForRowAtIndexPath de una manera similar, de modo que el elemento recupero proviene de la FRC derecha:
Item *item;
if (indexPath.section < firstFRC.sections.count) {
item = [firstFRC objectAtIndexPath:indexPath];
}
else {
item = [secondFRC objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
}
[[cell textLabel] setText:[item name]];
…rest of cell configuration
Esto funciona muy bien cuando inicio . Los Tableview muestra exactamente como se había previsto:
El problema (por fin)
La (primera) problema viene cuando selecciono la marca de verificación de un elemento no cobrados. Espero que el artículo se elimine del departamento en el que figura, y se mueva a la sección "recopilada".En su lugar, me sale:
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
Si intento lo contrario, entonces recibo un error diferente:
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
Sospecho que en ambos casos hay un problema con la consistencia con el número de secciones/filas en los FRC y la vista de tabla cuando un elemento se mueve de un FRC a otro. Aunque ese segundo error me hace pensar que tal vez haya un problema más simple relacionado con mi recuperación de artículos.
Cualquier dirección o idea sería apreciada. Puedo proporcionar más de mi código si me ayudaría, y también he creado una pequeña aplicación de prueba con una sola vista para demostrar el problema. Puedo subirlo si es necesario, pero sobre todo quería probar el problema en una caja de arena de pequeña escala.
Actualización - código adicional solicitada
a lo solicitado, esto es lo que sucede cuando se ha marcado tocó:
- (void)checkButtonTapped:(id)sender event:(id)event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];
if (indexPath != nil)
{
[self tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
}
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
Item *item;
if (indexPath.section < firstFRC.sections.count) {
item = [firstFRC objectAtIndexPath:indexPath];
}
else {
item = [secondFRC objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIButton *button = (UIButton *)cell.accessoryView;
if (![item collected]) {
[item setCollected:YES];
[button setBackgroundImage:[UIImage imageNamed:@"checked.png"] forState:UIControlStateNormal];
}
else if ([item collected]){
[item setCollected:NO];
[button setBackgroundImage:[UIImage imageNamed:@"unchecked.png"] forState:UIControlStateNormal];
}
NSError *error = nil;
if (![item.managedObjectContext save:&error]) {
NSLog(@"Error saving collected items");
}
}
Nota: los nombres de los departamentos están ligeramente apagados en la captura de pantalla. Olvidé usar el departamento como el primer descriptor de clasificación. –
¿Cómo está recargando su mesa? –
Publica el código que se llama cuando se toca una marca de verificación. – jcm