reloadItemsAtIndexPaths後のUICollectionViewのアニメーションを回避する


91

UICollectionViewは、reloadItemsAtIndexPathsが呼び出された後にアイテムをアニメーション化します(フェードアニメーション)。

このアニメーションを回避する方法はありますか?

iOS 6

回答:


231

iOS 7以降をターゲットにしている場合は、新しいUIView方法を使用できることに注意してくださいperformWithoutAnimation:。内部的には、これはここでの他の回答とほとんど同じように機能していると思います(UIViewアニメーション/コアアニメーションアクションを一時的に無効にします)が、構文は素晴らしくクリーンです。

したがって、特にこの質問については...

Objective-C:

[UIView performWithoutAnimation:^{
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
}];


迅速:

UIView.performWithoutAnimation {
    self.collectionView.reloadItemsAtIndexPaths(indexPaths)
}


もちろん、この原則は、変更がされていることを確認したいことをどのような状況に適用することが可能ではないアニメーション。


3
これは、iOS7以降で私が受け入れた答えよりもうまく機能しました。
フィリップサブーリン2014年

コレクションビューだけでなく、すべてのアニメーションを無効にします
user2159978 2014

10
@ user2159978その通りです。ここでのすべての回答は、UIViewアニメーションを一時的に無効にします。アニメーションは、渡されたブロック内から開始されたアクションに対してのみ無効になります。この場合は、コレクションビューのリロードのみです。それは尋ねられている質問に対する完全に有効な答えなので、私はそれが反対票に値するとは思わない。
スチュアート

信じられないほどのソリューション。animateWithDuration:0の代わりにこれを使用すると、itemSizeのないセルフサイズのコレクションビューセルを使用するときに、すばやく表示されるレイアウトの不具合を防ぐことができます
Ethan Gill

1
このソリューションは、私がreloadDataを使用して、このブロックの内側に、iOSの14でもう動作しませんが、iOSの13でそれが働いた
Maray97

161

これを試すこともできます:

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];
}];

挿入と削除の間にiOS7の弾力のあるアニメーションを使用したい場合、これは非常にクールです!


1
ありがとうございました。これは、uicollectionviewセルにストップウォッチタイマーを追加するのに非常に役立ちました。
hatunike 2013

鮮やかさ!これをinsertCellsで使用し、キーボードを上にして、セルが挿入された後にcollectionViewを新しいオフセットにアニメーション化しました。
David H

5
ピーターが言うように、これは他のアニメーションに干渉します。代わりに、完了ブロックの外で[UIView setAnimationsEnabled:YES]を呼び出す必要があります。そうすれば、その1つのアニメーションだけを防ぐことができます。
Adlai Holler 2014年

2
まったく同じことを行う別の方法を追加しました。
サム

1
ラッピングperformBatchUpdates内部はanimateWithDuration素晴らしいです!ヒントをありがとう!
ロバート

19

UICollectionViewは、reloadItemsAtIndexPathsが呼び出された後にアイテムをアニメーション化します(フェードアニメーション)。

このアニメーションを回避する方法はありますか?

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
    }

}

これは非常に古い質問なので、おそらくiOS6をターゲットにしていないでしょう。私は個人的にtvOS11に取り組んでいて、同じ質問をしたので、これは同じ問題を抱えている人のためにここにあります。


1
「これは本当にうまくいく!」という効果に対するこの回答には、3つか4つのコメントがありました。誰かがコメントを削除することにしました。実際、これはこれを達成するための現代的でハッキングのない方法であり、それらのコメントはこれを認識したものだと思います。
マットMcの

8

それを行うために、UICollectionViewカテゴリを作成しました。秘訣は、リロード中にすべてのアニメーションを無効にすることです。

if (!animated) {
    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
}

[self reloadItemsAtIndexPaths:indexPaths];

if (!animated) {
    [CATransaction commit];
}

また、Appleから、これはアニメーションを実行するべきではないという応答があります。そうであれば、バグです。何か間違ったことをしているのか、それともバグなのかわかりません。
Marcin 2013年

2
CATransaction.setDisableActions(true)これの省略形として行うこともできます。
CIFilter 2017年

5
extension UICollectionView {
    func reloadWithoutAnimation(){
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        self.reloadData()
        CATransaction.commit()
    }
}

これは迅速な3構文です
Amjad Tubasi 2017年

これはうまく機能し、UIView.performWithoutAnimationよりもさらに優れていました
Lance Samaria

5

これは、performBatchUpdatesアニメーションなしのSwift3バージョン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

        })
    }
}

2
- (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];
    }

}

1

$ 0.02を追加するために、選択した回答の両方のバージョンを試しましたが、元の方法の方が目的に適していました。私は、ユーザーが特定の週にカレンダーを入力し、前後にスワイプしてリストをフィルタリングするために個々の日を選択できるようにする、無限スクロールのカレンダービューに取り組んでいます。

私の実装では、古いデバイスでパフォーマンスを維持するために、カレンダービューを表す日付の配列を比較的小さく保つ必要があります。つまり、ユーザーが3週目の中央にいる状態で、約5週間分の日付を保持する必要があります。2番目のアプローチを使用する場合の問題は、アニメーションなしでコレクションビューを中央にスクロールして戻す必要がある、2番目のステップがあることです。これにより、ブロックされたベースアニメーションで、何らかの理由で非常にギザギザの外観になります。

私のコード:

[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];

0
 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)
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.