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];
「 iOS用コレクションビュープログラミングガイド 」の セクションとアイテムの挿入、削除、移動 を参照してください。
単一のセクションまたはアイテムを挿入、削除、または移動するには、次の手順を実行する必要があります。
- データソースオブジェクトのデータを更新します。
- コレクションビューの適切なメソッドを呼び出して、セクションまたはアイテムを挿入または削除します。
変更をコレクションビューに通知する前に、データソースを更新することが重要です。コレクションビューメソッドは、データソースに現在正しいデータが含まれていることを前提としています。そうでない場合、コレクションビューはデータソースから間違ったアイテムセットを受け取るか、存在しないアイテムを要求してアプリをクラッシュさせる可能性があります。
そのため、あなたの場合、最初にコレクションビューのデータソースに画像を追加してから、insertItemsAtIndexPaths
を呼び出す必要があります。コレクションビューは、データソースデリゲート関数に、挿入されたアイテムのビューを提供するように要求します。
これを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)
}
私はインデックスからアイテムを削除するときに同様の問題に直面していましたが、これは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];
}];
これにより、すべてのクラッシュおよびアサーションエラーを削除できます。