UICollectionView Realizando actualizaciones usando performBatchUpdates

Tengo un UICollectionView que estoy tratando de insert elementos en él dinámicamente / con animation. Así que tengo alguna function que descarga imágenes de forma asíncrona y me gustaría insert los elementos en lotes.

Una vez que tenga mis datos, me gustaría hacer lo siguiente:

 [self.collectionView performBatchUpdates:^{ for (UIImage *image in images) { [self.collectionView insertItemsAtIndexPaths:****] } } completion:nil]; 

Ahora en lugar del *** , debería pasar una matriz de NSIndexPaths , que debería apuntar a la location de los nuevos elementos que se van a insert. Estoy muy confundido ya que después de proporcionar la location, ¿cómo proporciono la image real que debería mostrarse en esa position?

Gracias


ACTUALIZAR:

resultsSize contiene el tamaño de la matriz fuente de datos, self.results , antes de agregar nuevos datos de los datos en newImages .

 [self.collectionView performBatchUpdates:^{ int resultsSize = [self.results count]; [self.results addObjectsFromArray:newImages]; NSMutableArray *arrayWithIndexPaths = [NSMutableArray array]; for (int i = resultsSize; i < resultsSize + newImages.count; i++) [arrayWithIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]]; [self.collectionView insertItemsAtIndexPaths:arrayWithIndexPaths]; } completion:nil]; 

Consulte Insertar, eliminar y mover secciones y elementos de la " Guía de progtwigción de la vista de collections para iOS ":

Para insert, eliminar o mover una sola sección o elemento, debe seguir estos pasos:

  1. Actualice los datos en su object fuente de datos.
  2. Llame al método apropiado de la vista de colección para insert o eliminar la sección o elemento.

Es fundamental que actualice su fuente de datos antes de notificar la vista de la recostackción de los cambios. Los methods de vista de colección suponen que su fuente de datos contiene los datos actualmente correctos. Si no lo hace, la vista de recostackción puede recibir el set incorrecto de elementos de su fuente de datos o solicitar elementos que no están allí y bloquear su aplicación.

Entonces, en su caso, primero debe agregar una image a la fuente de datos de la vista de colección y luego llamar a insertItemsAtIndexPaths . La vista de colección pedirá a la function de delegado de origen de datos que proporcione la vista del elemento insertado.

Acabo de implementar eso con Swift. Entonces me gustaría compartir mi implementación. Primero inicialice una matriz de NSBlockOperations:

  var blockOperations: [NSBlockOperation] = [] 

En el controller cambiará, reinicie la matriz:

 func controllerWillChangeContent(controller: NSFetchedResultsController) { blockOperations.removeAll(keepCapacity: false) } 

En el método did change object:

  func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { if type == NSFetchedResultsChangeType.Insert { println("Insert Object: \(newIndexPath)") blockOperations.append( NSBlockOperation(block: { [weak self] in if let this = self { this.collectionView!.insertItemsAtIndexPaths([newIndexPath!]) } }) ) } else if type == NSFetchedResultsChangeType.Update { println("Update Object: \(indexPath)") blockOperations.append( NSBlockOperation(block: { [weak self] in if let this = self { this.collectionView!.reloadItemsAtIndexPaths([indexPath!]) } }) ) } else if type == NSFetchedResultsChangeType.Move { println("Move Object: \(indexPath)") blockOperations.append( NSBlockOperation(block: { [weak self] in if let this = self { this.collectionView!.moveItemAtIndexPath(indexPath!, toIndexPath: newIndexPath!) } }) ) } else if type == NSFetchedResultsChangeType.Delete { println("Delete Object: \(indexPath)") blockOperations.append( NSBlockOperation(block: { [weak self] in if let this = self { this.collectionView!.deleteItemsAtIndexPaths([indexPath!]) } }) ) } } 

En el método did change section:

 func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { if type == NSFetchedResultsChangeType.Insert { println("Insert Section: \(sectionIndex)") blockOperations.append( NSBlockOperation(block: { [weak self] in if let this = self { this.collectionView!.insertSections(NSIndexSet(index: sectionIndex)) } }) ) } else if type == NSFetchedResultsChangeType.Update { println("Update Section: \(sectionIndex)") blockOperations.append( NSBlockOperation(block: { [weak self] in if let this = self { this.collectionView!.reloadSections(NSIndexSet(index: sectionIndex)) } }) ) } else if type == NSFetchedResultsChangeType.Delete { println("Delete Section: \(sectionIndex)") blockOperations.append( NSBlockOperation(block: { [weak self] in if let this = self { this.collectionView!.deleteSections(NSIndexSet(index: sectionIndex)) } }) ) } } 

Y, por último, en el controller sí, cambió el método de contenido:

 func controllerDidChangeContent(controller: NSFetchedResultsController) { collectionView!.performBatchUpdates({ () -> Void in for operation: NSBlockOperation in self.blockOperations { operation.start() } }, completion: { (finished) -> Void in self.blockOperations.removeAll(keepCapacity: false) }) } 

Personalmente, agregué algún código en el método deinit también, para cancelar las operaciones cuando el ViewController está a punto de ser desasignado:

 deinit { // Cancel all block operations when VC deallocates for operation: NSBlockOperation in blockOperations { operation.cancel() } blockOperations.removeAll(keepCapacity: false) } 

Estaba enfrentando el problema similar al eliminar el elemento del índice y esto es lo que creo que tenemos que hacer al usar performBatchUpdates: method.

1 # primera llamada deleteItemAtIndexPath para eliminar el elemento de la vista de colección.

2 # Elimina el elemento de la matriz.

3 # Actualiza la vista de recostackción recargando datos.

 [self.collectionView performBatchUpdates:^{ NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0]; [self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]]; [self.addNewDocumentArray removeObjectAtIndex:sender.tag]; } completion:^(BOOL finished) { [self.collectionView reloadData]; }]; 

Esto me ayuda a eliminar todas las fallas de falla y aserción.