在 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更新导致视图重叠问题

iOS 在CollectionView上做展开收起动画