UICollectionView Realizar actualizaciones usando performBatchUpdates


Tengo un UICollectionView que estoy tratando de insertar elementos en él dinámicamente/con animación. Así que tengo alguna función que descarga imágenes de forma asíncrona y me gustaría insertar 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 de ***, debería pasar una matriz de NSIndexPaths, que debería apuntar a la ubicación de los nuevos elementos que se insertarán. Estoy muy confundido ya que después de proporcionar la ubicación, ¿cómo puedo proporcionar el imagen real que se debe mostrar en esa posición?

Gracias


ACTUALIZACIÓN:

resultsSize contiene el tamaño de la matriz de origen de datos, self.results, antes de que se agreguen 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];
Author: zanona, 2012-09-30

3 answers

Consulte Insertar, Eliminar y Mover Secciones y elementos de la " Guía de programación de Vista de colección para iOS":

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

  1. Actualice los datos de su objeto de origen de datos.
  2. Llame al método apropiado de la vista de colección para insertar o eliminar la sección o elemento.

Es fundamental que actualice su fuente de datos antes de notificar el vista de colección de cualquier cambio. Los métodos de vista de colección asumen que su fuente de datos contiene los datos correctos actualmente. Si lo hace no, la vista de colección podría recibir el conjunto incorrecto de elementos de su fuente de datos o pedir elementos que no están allí y bloquear su app.

Así que en su caso, primero debe agregar una imagen a la fuente de datos de la vista de colección y luego llamar a insertItemsAtIndexPaths. La vista de colección pedirá a la función delegado de origen de datos que proporcione la vista para el elemento insertado.

 29
Author: Martin R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-14 12:35:05

Acabo de implementarlo con Swift. Así que me gustaría compartir mi implementación. Primero inicializar una matriz de NSBlockOperations:

    var blockOperations: [NSBlockOperation] = []

En el controlador cambiará, vuelva a iniciar 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 de la sección did change:

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 finalmente, en el controlador did 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)
}
 10
Author: Plot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-05 12:45:16

Me enfrentaba a un problema similar al eliminar el elemento del índice y esto es lo que creo que debemos hacer mientras usamos el método performBatchUpdates:.

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

2# Eliminar el elemento del array.

3# Actualizar la vista de la colecció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 todos los fallos de bloqueo y aserción.

 6
Author: Nik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-10 11:05:43