UICollectionView Выполнение обновлений с помощью PerformBatchUpdates
-
11-12-2019 - |
Вопрос
у меня есть UICollectionView
который я пытаюсь вставить в него элементы динамически/с анимацией.Итак, у меня есть функция, которая асинхронно загружает изображения и мне хотелось бы вставлять элементы пакетами.
Получив данные, я хотел бы сделать следующее:
[self.collectionView performBatchUpdates:^{
for (UIImage *image in images) {
[self.collectionView insertItemsAtIndexPaths:****]
}
} completion:nil];
Теперь вместо ***
, я должен передать массив NSIndexPaths
, который должен указывать на расположение новых элементов, которые нужно вставить.Я очень запутался, поскольку после указания местоположения, как мне предоставить фактическое изображение, которое должно отображаться в этом месте?
Спасибо
ОБНОВЛЯТЬ:
resultsSize
содержит размер массива источника данных, self.results
, прежде чем новые данные будут добавлены из данных в 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];
Решение
Видеть Вставка, удаление и перемещение разделов и элементов из "Руководство по программированию Collection View для 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))
}
})
)
}
}
И, наконец, в контроллере Did был изменен метод содержимого:
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)
})
}
Я лично также добавил некоторый код в метод deinit, чтобы отменить операции, когда ViewController вот-вот будет освобожден:
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];
}];
Это поможет мне удалить все сбои и ошибки утверждения.