web-dev-qa-db-ja.com

UICollectionView performBatchUpdatesを使用した更新の実行

UICollectionViewがあり、これを動的に/アニメーションでアイテムを挿入しようとしています。そのため、非同期で画像をダウンロードする機能があり、アイテムをバッチで挿入したいと思います。

データを取得したら、次のことを行います。

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

***の代わりに、NSIndexPathsの配列を渡す必要があります。これは、挿入される新しいアイテムの場所を指す必要があります。場所を提供した後、その位置に表示される実際の画像をどのように提供するのですか?

ありがとうございました


更新:

resultsSizeには、newImagesのデータから新しいデータが追加される前のデータソース配列self.resultsのサイズが含まれます。

[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

iOS用コレクションビュープログラミングガイド 」の セクションとアイテムの挿入、削除、移動 を参照してください。

単一のセクションまたはアイテムを挿入、削除、または移動するには、次の手順を実行する必要があります。

  1. データソースオブジェクトのデータを更新します。
  2. コレクションビューの適切なメソッドを呼び出して、セクションまたはアイテムを挿入または削除します。

変更をコレクションビューに通知する前に、データソースを更新することが重要です。コレクションビューメソッドは、データソースに現在正しいデータが含まれていることを前提としています。そうでない場合、コレクションビューはデータソースから間違ったアイテムセットを受け取るか、存在しないアイテムを要求してアプリをクラッシュさせる可能性があります。

そのため、あなたの場合、最初にコレクションビューのデータソースに画像を追加してから、insertItemsAtIndexPathsを呼び出す必要があります。コレクションビューは、データソースデリゲート関数に、挿入されたアイテムのビューを提供するように要求します。

30
Martin R

これをSwiftで実装しました。そこで、実装を共有したいと思います。 NSBlockOperationsの配列を最初に初期化します。

    var blockOperations: [NSBlockOperation] = []

コントローラーが変更されたら、アレイを再初期化します。

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

オブジェクトメソッドを変更しました:

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

変更セクションのメソッドで:

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

そして最後に、コントローラーではコンテンツメソッドを変更しました:

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

ViewControllerが割り当て解除されようとしているときに操作をキャンセルするために、deinitメソッドにもいくつかのコードを個人的に追加しました。

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

    blockOperations.removeAll(keepCapacity: false)
}
11
Plot

私はインデックスからアイテムを削除するときに同様の問題に直面していましたが、これはperformBatchUpdates:メソッドを使用しているときに行う必要があると思います。

1#最初にdeleteItemAtIndexPathを呼び出して、コレクションビューからアイテムを削除します。

2#配列から要素を削除します。

3#データをリロードしてコレクションビューを更新します。

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

これにより、すべてのクラッシュおよびアサーションエラーを削除できます。

6
Nik