在 reloadItemsAtIndexPaths 之后避免 UICollectionView 的动画
Posted
技术标签:
【中文标题】在 reloadItemsAtIndexPaths 之后避免 UICollectionView 的动画【英文标题】:Avoid animation of UICollectionView after reloadItemsAtIndexPaths 【发布时间】:2012-12-15 04:54:56 【问题描述】:在调用 reloadItemsAtIndexPaths 后 UICollectionView 对项目进行动画处理(淡入淡出动画)。
有没有办法避免这个动画?
ios 6
【问题讨论】:
【参考方案1】:不要使用reloadData()
,而是尝试以下重新加载所有可见单元格而不使用动画。
self.collectionView.reloadItems(at: self.collectionView.indexPathsForVisibleItems)
【讨论】:
【参考方案2】: func reloadRowsWithoutAnimation(at indexPaths: [IndexPath])
let contentOffset = collectionView.contentOffset
UIView.setAnimationsEnabled(false)
collectionView.performBatchUpdates
collectionView.reloadItems(at: indexPaths)
UIView.setAnimationsEnabled(true)
collectionView.setContentOffset(contentOffset, animated: false)
【讨论】:
【参考方案3】:在调用 reloadItemsAtIndexPaths 后 UICollectionView 动画项目 (淡出动画)。
有没有办法避免这个动画?
iOS 6
我假设您使用的是 FlowLayout。由于您试图摆脱淡入淡出动画,请尝试以下操作:
import UIKit
class NoFadeFlowLayout: UICollectionViewFlowLayout
override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?
let attrs = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
attrs?.alpha = 1.0
return attrs
override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?
let attrs = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath)
attrs?.alpha = 1.0
return attrs
这是一个非常古老的问题,因此您可能不再针对 iOS 6。我个人在 tvOS 11 上工作并且有同样的问题,所以这里是为遇到同样问题的任何人准备的。
【讨论】:
这个答案有 3 或 4 个 cmets,效果是“哇,这真的很好用!”有人决定移除 cmets。我认为,事实上,这是实现这一点的现代且非 hacky 的方式,而这些 cmets 是对这一点的认可。【参考方案4】:这是performBatchUpdates
的Swift 3 版本,没有UICollectionView
的动画。我发现这比collectionView.reloadData()
更适合我,因为它减少了插入记录时的单元格交换。
func appendCollectionView(numberOfItems count: Int)
// calculate indexes for the items to be added
let firstIndex = dataItems.count - count
let lastIndex = dataItems.count - 1
var indexPaths = [IndexPath]()
for index in firstIndex...lastIndex
let indexPath = IndexPath(item: index, section: 0)
indexPaths.append(indexPath)
UIView.performWithoutAnimation
self.collectionView.performBatchUpdates( () -> Void in
self.collectionView.insertItems(at: indexPaths)
, completion: (finished) -> Void in
)
【讨论】:
【参考方案5】:extension UICollectionView
func reloadWithoutAnimation()
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
self.reloadData()
CATransaction.commit()
【讨论】:
这是 swift 3 语法 这个效果很好,甚至比 UIView.performWithoutAnimation 更好【参考方案6】:值得注意的是,如果您的目标是 iOS 7 及更高版本,您可以使用新的UIView
方法performWithoutAnimation:
。我怀疑在后台这与此处的其他答案大致相同(暂时禁用UIView
动画/核心动画操作),但语法很好而且干净。
所以对于这个问题特别...
目标-C:
[UIView performWithoutAnimation:^
[self.collectionView reloadItemsAtIndexPaths:indexPaths];
];
斯威夫特:
UIView.performWithoutAnimation
self.collectionView.reloadItemsAtIndexPaths(indexPaths)
当然,此原则可应用于您希望确保更改动画化的任何情况。
【讨论】:
这比我在 iOS 7+ 上接受的答案更好。 它不仅禁用了集合视图的所有动画 @user2159978 没错;这里的所有答案都暂时禁用 UIView 动画。只有从传入的块中启动的操作才会禁用动画,在这种情况下只是集合视图的重新加载。这是对所提问题的完全有效答案,所以我认为它不值得投反对票。 难以置信的解决方案。使用它而不是 animateWithDuration:0 可以防止在使用没有 itemSize 的自行调整大小的集合视图单元格时出现快速但可见的布局故障 这个解决方案在 iOS 14 上不再有效,在这个块内我使用 reloadData,但在 iOS 13 上它有效【参考方案7】:只是为了添加我的 0.02 美元,我尝试了所选答案的两个版本,并且原始方式更适合我的目的。我正在开发一个无限滚动的日历视图,它允许用户在给定的一周输入日历,然后来回滑动并选择单独的日子来过滤列表。
在我的实现中,为了在旧设备上保持性能,代表日历视图的日期数组必须保持相对较小,这意味着保留大约 5 周的日期,用户在第三天的中间星期。使用第二种方法的问题是,在第二步中,您必须在没有动画的情况下将集合视图滚动回中间,这会由于某些原因导致基础动画被阻塞,从而导致外观非常参差不齐。
我的代码:
[UIView setAnimationsEnabled:NO];
[self.collectionView performBatchUpdates:^
[self.collectionView deleteItemsAtIndexPaths:indexPathDeleteArray];
[self.collectionView insertItemsAtIndexPaths:indexPathAddArray];
completion:NULL];
[UIView setAnimationsEnabled:YES];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:14 inSection:0];
[self.collectionView scrollToItemAtIndexPath:newIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
【讨论】:
【参考方案8】:你也可以试试这个:
UICollectionView *collectionView;
...
[UIView setAnimationsEnabled:NO];
[collectionView performBatchUpdates:^
[collectionView reloadItemsAtIndexPaths:indexPaths];
completion:^(BOOL finished)
[UIView setAnimationsEnabled:YES];
];
编辑:
我还发现,如果将performBatchUpdates
包装在 UIView 动画块中,则会使用 UIView 动画而不是默认动画,因此您可以将动画持续时间设置为 0,如下所示:
[UIView animateWithDuration:0 animations:^
[collectionView performBatchUpdates:^
[collectionView reloadItemsAtIndexPaths:indexPaths];
completion:nil];
];
如果您想在插入和删除期间使用 iOS 7 弹性动画,这将非常酷!
【讨论】:
谢谢。这对于将秒表计时器添加到 uicollectionview 单元格非常有帮助。 太棒了!我将它与 insertCells 一起使用,并在键盘向上的情况下,在插入单元格后将 collectionView 设置为新的偏移量。 正如彼得所说,这会干扰其他动画。相反,您应该在完成块之外调用 [UIView setAnimationsEnabled:YES]。这样你只能阻止那 1 个动画。 我添加了另一种方法,它的作用完全相同。 将performBatchUpdates
包裹在animateWithDuration
内非常棒!感谢您的提示!【参考方案9】:
- (void)reloadCollectionViewAnimated:(BOOL)animated
if (animated)
[self.collectionView performBatchUpdates:^
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
completion:^(BOOL finished)
];
else
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
[CATransaction commit];
【讨论】:
【参考方案10】:我写了一个category on UICollectionView 来做到这一点。诀窍是在重新加载时禁用所有动画:
if (!animated)
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
[self reloadItemsAtIndexPaths:indexPaths];
if (!animated)
[CATransaction commit];
【讨论】:
我也得到了苹果的回应,说这不应该做任何动画,如果是这样,那就是一个错误。我不知道是我做错了什么还是一个错误。 你也可以只用CATransaction.setDisableActions(true)
作为这个的简写。以上是关于在 reloadItemsAtIndexPaths 之后避免 UICollectionView 的动画的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView reloadItemsAtIndexPaths时 报错
collectionView使用reloadData和reloadItemsAtIndexPaths冲突
使用 reloadItemsAtIndexPaths 重新加载 UICollectionViewCell 的 footerView
如果 dataSource 计数可能发生变化,是不是可以同时使用 reloadItemsAtIndexPaths 和 reloadData?
iOS解决UICollectionView中使用reloadItemsAtIndexPaths进行局部cell更新导致视图重叠问题