Hindari animasi UICollectionView setelah reloadItemsAtIndexPaths

91

UICollectionView menganimasikan item setelah reloadItemsAtIndexPaths dipanggil (animasi fade).

Adakah cara untuk menghindari animasi ini?

iOS 6

Marcin
sumber

Jawaban:

231

Perlu dicatat bahwa jika Anda menargetkan iOS 7 ke atas, Anda dapat menggunakan UIViewmetode baru performWithoutAnimation:. Saya menduga bahwa di bawah tenda ini melakukan hal yang sama dengan jawaban lain di sini (sementara menonaktifkan UIViewanimasi / tindakan Animasi Inti), tetapi sintaksnya bagus dan bersih.

Jadi untuk pertanyaan ini khususnya ...

Objective-C:

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


Cepat:

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


Tentu saja prinsip ini dapat diterapkan untuk situasi apa pun yang Anda inginkan untuk memastikan perubahan tidak dianimasikan.

Stuart
sumber
3
Ini bekerja lebih baik daripada jawaban yang diterima untuk saya di iOS 7+.
Philippe Sabourin
Menonaktifkan semua animasi tidak hanya untuk tampilan koleksi
user2159978
10
@ user2159978 Itu benar; semua jawaban di sini untuk sementara menonaktifkan animasi UIView. Animasi hanya dinonaktifkan untuk tindakan yang dimulai dari dalam blok yang diteruskan, dalam hal ini hanya pemuatan ulang tampilan koleksi. Ini adalah jawaban yang benar-benar valid untuk pertanyaan yang diajukan, jadi menurut saya itu tidak pantas untuk ditolak.
Stuart
Solusi yang luar biasa. Menggunakan ini alih-alih animateWithDuration: 0 mencegah kesalahan tata letak yang cepat tetapi terlihat saat menggunakan sel tampilan koleksi self-sizing tanpa itemSize
Ethan Gill
1
Solusi ini tidak berfungsi lagi di iOS 14, di dalam blok ini saya menggunakan reloadData, tetapi dengan iOS 13 berhasil
Maray97
161

Anda juga bisa mencoba ini:

UICollectionView *collectionView;

...

[UIView setAnimationsEnabled:NO];

[collectionView performBatchUpdates:^{
    [collectionView reloadItemsAtIndexPaths:indexPaths];
} completion:^(BOOL finished) {
    [UIView setAnimationsEnabled:YES];
}];

Edit:

Saya juga menemukan bahwa jika Anda membungkus performBatchUpdatesdalam blok animasi UIView, animasi UIView digunakan sebagai pengganti animasi default, jadi Anda bisa mengatur durasi animasi ke 0, seperti:

[UIView animateWithDuration:0 animations:^{
    [collectionView performBatchUpdates:^{
        [collectionView reloadItemsAtIndexPaths:indexPaths];
    } completion:nil];
}];

Ini sangat keren jika Anda ingin menggunakan animasi kenyal iOS 7 selama memasukkan dan menghapus!

Sam
sumber
1
Terima kasih. Ini sangat membantu dalam menambahkan timer stopwatch ke sel uicollectionview.
hatunike
Cemerlang! Saya menggunakan ini dengan insertCells, dan dengan keyboard di atas, untuk menganimasikan collectionView ke offset baru setelah sel dimasukkan.
David H
5
Seperti yang dikatakan Peter, ini mengganggu animasi lain. Sebaliknya, Anda harus memanggil [UIView setAnimationsEnabled: YES] di luar blok penyelesaian. Dengan begitu Anda hanya mencegah 1 animasi itu.
Adlai Holler
2
Saya menambahkan metode alternatif, yang melakukan hal yang persis sama.
Sam
1
Membungkus performBatchUpdatesbagian dalam animateWithDurationitu brilian! Terima kasih atas tipnya!
Robert
19

UICollectionView menganimasikan item setelah reloadItemsAtIndexPaths dipanggil (animasi fade).

Adakah cara untuk menghindari animasi ini?

iOS 6

Saya berasumsi Anda menggunakan FlowLayout. Karena Anda mencoba menghilangkan animasi pudar, coba ini:

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
    }

}

Ini adalah pertanyaan yang sangat lama, jadi Anda mungkin tidak menargetkan iOS 6 lagi. Saya secara pribadi bekerja di tvOS 11 dan memiliki pertanyaan yang sama, jadi ini di sini untuk siapa saja yang datang dengan masalah yang sama.

Matt Mc
sumber
1
Ada 3 atau 4 komentar pada jawaban ini sebagai efek dari "Wow ini bekerja dengan sangat baik!" Seseorang memutuskan untuk menghapus komentar tersebut. Saya pikir, sebenarnya, ini adalah cara modern dan non-hacky untuk mencapai hal ini, dan komentar tersebut adalah pengakuan akan hal ini.
Matt Mc
8

Saya menulis kategori di UICollectionView untuk melakukan hal itu. Triknya adalah menonaktifkan semua animasi saat memuat ulang:

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

[self reloadItemsAtIndexPaths:indexPaths];

if (!animated) {
    [CATransaction commit];
}
Giorgio Calderolla
sumber
Saya juga mendapat respon dari Apple bahwa ini tidak boleh melakukan animasi apapun, jika memang itu bug. Saya tidak tahu apakah saya melakukan sesuatu yang salah atau itu bug.
Marcin
2
Anda juga bisa melakukannya CATransaction.setDisableActions(true)sebagai singkatan untuk ini.
CIFilter
5
extension UICollectionView {
    func reloadWithoutAnimation(){
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        self.reloadData()
        CATransaction.commit()
    }
}
Amjad Tubasi
sumber
ini adalah sintaks cepat 3
Amjad Tubasi
ini bekerja dengan baik, bahkan lebih baik daripada UIView.performWithoutAnimation
Lance Samaria
5

Ini adalah versi Swift 3 performBatchUpdatestanpa animasi ke UICollectionView. Saya menemukan ini bekerja lebih baik untuk saya daripada collectionView.reloadData()karena mengurangi pertukaran sel saat rekaman dimasukkan.

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

        })
    }
}
markhorrocks
sumber
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];
    }

}
Peter Lapisu
sumber
1

Hanya untuk menambahkan $ 0,02 saya, saya mencoba kedua versi jawaban yang dipilih, dan cara asli bekerja lebih baik untuk tujuan saya. Saya sedang mengerjakan tampilan kalender gulir tak terbatas yang memungkinkan pengguna untuk masuk ke kalender pada minggu tertentu dan kemudian menggesek bolak-balik dan memilih hari individu untuk memfilter daftar.

Dalam implementasi saya, untuk menjaga agar segala sesuatunya berjalan dengan baik pada perangkat yang lebih lama, susunan tanggal yang mewakili tampilan kalender harus dijaga agar relatif kecil yang berarti memegang tanggal sekitar 5 minggu, dengan pengguna di tengah pada minggu ke-3. Masalah dengan menggunakan pendekatan kedua adalah, ada langkah kedua di mana Anda harus menggulir tampilan koleksi kembali ke tengah tanpa animasi, yang membuat tampilan sangat bergerigi karena beberapa alasan dengan animasi dasar yang diblokir.

Kode Saya:

[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];
Matt S.
sumber
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)
    }
Alexandr Arsenyuk
sumber