2012-09-15 16 views
5

Pensé que got it. Pero un nuevo bloqueo que encontré en mi aplicación dice lo contrario. Entonces, cualquiera sabe el código realmente correcto para NSFetchedResultsChangeUpdate cuando newIndexPath no es nada y no es lo mismo que indexPath en -controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:?Cómo usar newIndexPath para NSFetchedResultsChangeUpdate?

+0

¿Cuál es el mensaje de error/stack backtrace? –

+0

newIndexPath está fuera de límite. – an0

+2

Tuve un problema similar, y mi solución/solución alternativa (?) No fue utilizar indexPath/newIndexPath en absoluto para un evento de actualización. Puede ver mi pregunta y la respuesta auto proporcionada aquí: http://stackoverflow.com/questions/11432556/nsrangeexception-exception-in-nsfetchedresultschangeupdate-event-of-nsfetchedres. Por favor, hágamelo saber si ayuda! –

Respuesta

8

Me encontré con un bloqueo en la actualización, y parece que newIndexPath se proporciona como una ruta de índice en los objetos del controlador de resultados obtenidos cuando esa ruta de índice no coincide con la ruta de índice necesaria para recuperar la celda de la tabla. Considere lo siguiente:

  1. Una vista de tabla tiene una sección en el índice 0 con 15 elementos.
  2. El elemento en el índice 13 (la segunda a la última opción) se suprime
  3. El elemento en el índice 14 (el último elemento) se actualiza

En el caso anterior, en el supuesto de que está utilizando [tableView beginUpdates] y [tableView endUpdates] en los métodos apropiados controllerWill/DidChangeContent:, necesitará usar el parámetro indexPath para recuperar la celda de la tabla para actualizar (que será la sección 0, índice 14) y el parámetro newIndexPath para recuperar el objeto para configurar la celda desde el controlador de resultados (que será la sección 0, índice 13).

Supongo que funciona de esta manera porque la eliminación parece haber sucedido ya en cuanto al controlador de resultados, pero no ha sucedido en la vista de tabla (debido a las llamadas beginUpdates/endUpdates envolviendo las actualizaciones). Tiene sentido si considera el caso anterior, pero parece que toda la documentación no considera este caso.

Así que la respuesta a la pregunta es que parece que debe usar el parámetro indexPath para recuperar una celda de la vista de tabla, y el parámetro newIndexPath para recuperar el objeto del controlador de resultados obtenidos. Tenga en cuenta que si no ha habido una inserción o eliminación, parece pasar nil para newIndexPath, por lo que en este caso deberá usar indexPath para ambos fines.

+0

En una reflexión posterior, me parece que NSFetchedController debería llamar al método de delegado con un evento de movimiento en lugar de un evento de actualización. Lo hace en el caso de prueba simple que acabo de crear. Para mí es un misterio en este momento qué condición desencadena una actualización. –

+0

Si leíste el artículo al que me he vinculado en la pregunta, deberías ver que es exactamente lo que hice. – an0

+0

Mi error, en realidad no vi el enlace hasta que lo señaló. ¿Qué línea de código está causando el colapso en su caso? ¿Qué NSIndexPath estás usando y qué haces con él? –

1

Si un objeto de NSFetchedResultsController cambia y se mueve al "mismo tiempo" -controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: envía tipo NSFetchedResultsChangeUpdate (see here).

Mi solución es cambiar el tipo de moverse cada vez que el tipo es actualizar y indexPath no es igual a newIndexPath

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath 
{ 
    UITableView *tableView; 

    if (controller == self.fetchedResultsController) { 
     tableView = self.tableView; 
    } 

    else { 
     tableView = self.searchDisplayController.searchResultsTableView; 
    } 

    // type is "update" ---> should be "move" 
    if (type == NSFetchedResultsChangeUpdate && [indexPath compare:newIndexPath] != NSOrderedSame && newIndexPath != nil) { 
     type = NSFetchedResultsChangeMove; 
    } 

    switch(type) { 
     case NSFetchedResultsChangeInsert: 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeUpdate: 
      [self fetchedResultsController:controller configureCell:(UITableViewCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft]; 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationRight]; 
      break; 
    } 
} 

Después tiene que actualizar la vista de tabla

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
{ 
[self.tableView endUpdates]; 

[self.tableView reloadData]; 
} 

espero que este ¡es útil!

+1

¿Por qué tienes un reloadData después de un endUpdates? – Frizlab

+0

En ese caso en que el tipo de cambio es "actualizar" pero debe ser "mover", la vista de tabla no se actualiza correctamente. Por lo tanto, debe volver a cargar la vista de tabla. Tal vez no sea necesario en iOS8 y superior, fue en iOS7. –

+0

He estado buscando esta respuesta durante años. Funciona perfecto. Gracias – Nekbeth

Cuestiones relacionadas