UICollectionView 动画数据更改

在我的项目中,我使用 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: ]来执行更改的动画,还是有更好的方法?

我没有太多的子类化 CollectionView,所以任何帮助将是巨大的..。

谢谢, 比尔。

92980 次浏览

reloadData doesn't animate, nor does it reliabably do so when put in a UIView animation block. It wants to be in a UICollecitonView performBatchUpdates block, so try something more like:

[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:^(BOOL finished) {
// do something on completion
}];

Wrapping -reloadData in -performBatchUpdates: does not seem to cause a one-section collection view to animate.

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

However, this code works:

[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];

The help text says:

Call this method to reload all of the items in the collection view. This causes the collection view to discard any currently visible items and redisplay them. For efficiency, the collection view only displays those cells and supplementary views that are visible. If the collection data shrinks as a result of the reload, the collection view adjusts its scrolling offsets accordingly. You should not call this method in the middle of animation blocks where items are being inserted or deleted. Insertions and deletions automatically cause the table’s data to be updated appropriately.

I think the key part is "causes the collection view to discard any currently visible items". How is it going to animate the movement of items it has discarded?

This is what I did to animate reload of ALL SECTIONS:

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

Reloading the whole collection view inside a performBatchUpdates:completion: block does a glitchy animation for me on iOS 9 simulator. If you have a specific UICollectionViewCell you want do delete, or if you have it's index path, you could call deleteItemsAtIndexPaths: in that block. By using deleteItemsAtIndexPaths:, it does a smooth and nice animation.

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


[self.collectionView performBatchUpdates:^{
[self.collectionView deleteItemsAtIndexPaths:@[[self.collectionView indexPathForCell:cell]]];
// or...
[self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:nil];

For Swift users, if your collectionview only has one section:

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

As seen on https://stackoverflow.com/a/42001389/4455570