web-dev-qa-db-fra.com

UICollectionView Exécution de mises à jour à l'aide de performBatchUpdates

J'ai un UICollectionView que j'essaie d'y insérer des éléments dynamiquement/avec animation. J'ai donc une fonction qui télécharge les images de manière asynchrone et souhaite insérer les éléments par lots.

Une fois que j'ai mes données, je voudrais faire ce qui suit:

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

Maintenant à la place du ***, Je devrais passer un tableau de NSIndexPaths, qui devrait pointer vers l'emplacement des nouveaux éléments à insérer. Je suis très confus car après avoir fourni l'emplacement, comment puis-je fournir l'image réelle qui devrait être affichée à cette position?

Merci


MISE À JOUR:

resultsSize contient la taille du tableau de source de données, self.results, avant l'ajout de nouvelles données à partir des données dans 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];
30
darksky

Voir Insertion, suppression et déplacement de sections et d'éléments dans le " Guide de programmation de la vue de collection pour iOS ":

Pour insérer, supprimer ou déplacer une seule section ou un seul élément, vous devez suivre ces étapes:

  1. Mettez à jour les données de votre objet source de données.
  2. Appelez la méthode appropriée de la vue de collection pour insérer ou supprimer la section ou l'élément.

Il est essentiel que vous mettiez à jour votre source de données avant d'aviser la vue de la collection de toute modification. Les méthodes d'affichage de la collection supposent que votre source de données contient les données actuellement correctes. Si ce n'est pas le cas, la vue de collection peut recevoir le mauvais ensemble d'éléments de votre source de données ou demander des éléments qui ne sont pas là et planter votre application.

Dans votre cas, vous devez donc d'abord ajouter une image à la source de données de la vue de collection, puis appeler insertItemsAtIndexPaths. La vue de collection demandera alors à la fonction déléguée de la source de données de fournir la vue de l'élément inséré.

30
Martin R

Je viens de l'implémenter avec Swift. Je voudrais donc partager mon implémentation. Initialisez d'abord un tableau de NSBlockOperations:

    var blockOperations: [NSBlockOperation] = []

Dans le contrôleur va changer, réinitialisez le tableau:

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

Dans la méthode 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!])
                }
            })
        )
    }
}

Dans la méthode de la section 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))
                }
            })
        )
    }
}

Et enfin, dans le contrôleur, a-t-il changé de méthode de contenu:

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)
    })
}

J'ai personnellement ajouté du code dans la méthode deinit également, afin d'annuler les opérations lorsque le ViewController est sur le point d'être désalloué:

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

    blockOperations.removeAll(keepCapacity: false)
}
11
Plot

J'étais confronté au même problème lors de la suppression de l'élément de l'index et voici ce que je pense que nous devons faire lors de l'utilisation de performBatchUpdates: méthode.

1 # appelez d'abord deleteItemAtIndexPath pour supprimer l'élément de la vue de collection.

2 # Supprimez l'élément du tableau.

3 # Mettre à jour la vue de la collection en rechargeant les données.

[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];
        }];

Cela m'aide à supprimer tous les échecs de plantage et d'assertion.

6
Nik