UICollectionView анимировать изменение данных

83

В моем проекте я использую UICollectionView для отображения сетки значков.

Пользователь может изменить порядок, щелкнув сегментированный элемент управления, который вызывает выборку из основных данных с другим NSSortDescriptor.

Объем данных всегда одинаков, просто попадает в разные разделы / строки:

- (IBAction)sortSegmentedControlChanged:(id)sender {

   _fetchedResultsController = nil;
   _fetchedResultsController = [self newFetchResultsControllerForSort];

   NSError *error;
   if (![self.fetchedResultsController performFetch:&error]) {
       NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
   }

   [self.collectionView reloadData];
}

Проблема в том, что reloadData не анимирует изменение, UICollectionView просто появляется с новыми данными.

Следует ли мне отслеживать, в каком indexPath находилась ячейка до и после изменения, и использовать [self.collectionView moveItemAtIndexPath: toIndexPath:] для выполнения анимации для изменения или есть лучший метод?

Я не особо разбирался в создании подклассов collectionViews, поэтому любая помощь будет отличной ...

Спасибо, Билл.

Нимрод7
источник
Вы пробовали обернуть этот reloadDataвызов в блок анимации?
Саймон

Ответы:

72

reloadData не анимируется и не делает этого надежно, когда помещается в блок анимации UIView. Он хочет быть в блоке UICollecitonView performBatchUpdates, поэтому попробуйте что-нибудь вроде:

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:^(BOOL finished) {
    // do something on completion 
}];
Полосы
источник
5
да, похоже, это работает, но только если у вас есть номера разделов сохранения ... иначе я получаю "недопустимое количество разделов. Количество разделов, содержащихся в представлении коллекции после обновления (10), должно быть равно количеству разделы, содержащиеся в представлении коллекции до обновления (16) ". Мне нужно вручную вставлять / удалять разделы ... все равно принято! Благодарю.
Nimrod7
1
Под «вручную» вы имеете в виду вызовы insertSections / moveSection из UICollectionView: toSection / deleteSections? Или что-то другое? (извините, до сих пор мое использование UICollectionView имело статическое количество разделов)
Stripes
1
да, с помощью этих методов ... это немного сложно реализовать мысль, вы должны запомнить пути индекса до и после обновления, число, которое было удалено, вставлено или перемещено ...
Nimrod7 01
12
Это не сработало для моей коллекции с одним разделом. Ошибок не было, но и анимации не было. Замена -reloadSections:заставляет это работать.
paulmelnikow 01
9
Этот ответ неверен, так как в моем случае установка reloadData в performBatchUpdates приведет к сбою моего приложения и сообщению, что количество ячеек не будет одинаковым до и после. использование reloadSections решило мою проблему. В документе Apple также указано, что в блоке анимации не следует вызывать reloadData
Wingzero
145

Обертывание -reloadDataв , -performBatchUpdates:кажется, не вызывает представление коллекции в один раздел анимировать.

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadData];
} completion:nil];

Однако этот код работает:

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];
Полмельников
источник
9
Примечание из документации: вы не должны вызывать метод перезагрузки данных в середине блоков анимации, где элементы вставляются или удаляются. Вставки и удаления автоматически вызывают соответствующее обновление данных таблицы.
Рафаэль Оливейра
я думаю, это должен быть правильный ответ. потому что это сработало для меня. У меня только 1 раздел, и это работает не правильный ответ.
Махеш Агравал,
Не работает, если еще и вставляешь. Отменит анимацию сортировки.
Андрес Канелла,
3
Стоит отметить, что вам не нужно self.collectionView performBatchUpdatesпереносить вызов reloadSectionsдля получения анимации. Фактически, performBatchUpdatesв некоторых случаях вызов может вызвать непреднамеренное мерцание / проблемы с изменением размера ячейки.
Paul Popiel
1
Или просто [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; reloadSectionsв виде коллекции или в виде таблицы, сделайте это с анимацией.
bauerMusic
67

Вот что я сделал, чтобы оживить перезагрузку ВСЕХ РАЗДЕЛОВ:

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)]];

Swift 3

let range = Range(uncheckedBounds: (0, collectionView.numberOfSections))
let indexSet = IndexSet(integersIn: range)
collectionView.reloadSections(indexSet)
Ярив Нисим
источник
8

Для пользователей Swift, если в вашем обзоре коллекции есть только один раздел:

self.collectionView.performBatchUpdates({
                    let indexSet = IndexSet(integersIn: 0...0)
                    self.collectionView.reloadSections(indexSet)
                }, completion: nil)

Как видно на https://stackoverflow.com/a/42001389/4455570

Габриэль Вамунью
источник
3

Перезагрузка всей коллекции внутри performBatchUpdates:completion:блока вызывает у меня глючную анимацию на симуляторе iOS 9. Если у вас есть конкретный объект, который UICollectionViewCellвы хотите удалить, или если у вас есть его индексный путь, вы можете вызвать deleteItemsAtIndexPaths:этот блок. С его помощью deleteItemsAtIndexPaths:получается плавная и приятная анимация.

UICollectionViewCell* cellToDelete = /* ... */;
NSIndexPath* indexPathToDelete = /* ... */;

[self.collectionView performBatchUpdates:^{
    [self.collectionView deleteItemsAtIndexPaths:@[[self.collectionView indexPathForCell:cell]]];
    // or...
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:nil];
Немезида
источник
1
Просто хочу упомянуть вещь, которая решила проблему сбоя в моем случае, обновите источник данных (то есть количество разделов / элементов) перед вызовом performUpdates в представлении коллекции.
ViruMax 05
Анимация из других операций; есть и НИКОГДА НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ не было анимации для reloadData. insertItems вставляет в порядке возрастания и выполняется ПОСЛЕ обновлений; deleteItems - наоборот.
Джеймс Буш
1

В справочном тексте говорится:

Вызовите этот метод, чтобы перезагрузить все элементы в представлении коллекции. Это заставляет представление коллекции отбрасывать все видимые в данный момент элементы и повторно отображать их. Для эффективности в представлении коллекции отображаются только те ячейки и дополнительные представления, которые видны. Если данные коллекции сжимаются в результате перезагрузки, представление коллекции соответствующим образом корректирует смещения прокрутки. Вы не должны вызывать этот метод в середине блоков анимации, где элементы вставляются или удаляются. Вставки и удаления автоматически вызывают соответствующее обновление данных таблицы.

Я думаю, что ключевая часть "заставляет представление коллекции отбрасывать все видимые в данный момент элементы". Как он будет анимировать движение отброшенных предметов?

Виктор Энгель
источник
Точно, это не так. reloadData - это двойная операция без анимации; это сначала операция удаления, а затем операция вставки.
Джеймс Буш