contentSize tidak diperbarui setelah reloadData dipanggil di UICollectionView

Adakah yang tahu mengapa contentSize tidak segera diperbarui setelah reloadData dipanggil di UICollectionView?

Jika Anda perlu mengetahui contentSize, solusi terbaik yang saya temukan adalah sebagai berikut:

[_collectionView reloadData];

double delayInSeconds = 0.0001;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void)
    {
        // TODO: Whatever it is you want to do now that you know the contentSize.
    });

Tentu saja ini adalah peretasan yang cukup rapuh yang membuat asumsi terhadap penerapan Apple, namun sejauh ini terbukti berhasil dengan cukup andal.

Adakah yang punya solusi atau pengetahuan lain tentang mengapa hal ini terjadi? Saya sedang berdebat untuk mengirimkan radar karena ini saya tidak mengerti mengapa mereka tidak dapat menghitung contentSize dalam run loop yang sama. Begitulah cara kerja UITableView untuk seluruh implementasinya.

EDIT: Pertanyaan ini digunakan untuk mereferensikan metode setContentOffset di dalam blok karena saya ingin menggulir tampilan koleksi di aplikasi saya. Saya telah menghapus pemanggilan metode karena jawaban orang berfokus pada mengapa saya tidak menggunakan scrollToItemAtIndexPath di dalam mengapa contentSize tidak diperbarui.


person Reid Main    schedule 30.09.2013    source sumber
comment
Saya pikir cara yang lebih tepat adalah menggunakan scrollToItemAtIndexPath:atScrollPosition:animated:. Mengenai mengapa ukuran konten tidak diperbarui, saya hanya dapat berasumsi ini merupakan pengoptimalan untuk mencegah pemanggilan penghitungan ukuran dinamis yang berpotensi banyak dan mahal atau mengembalikan informasi yang tidak akurat dan juga melakukan pemrosesan dan pekerjaan yang tidak perlu.   -  person Matt    schedule 01.10.2013
comment
Di aplikasi sebenarnya saya menggunakannya. Demi kesederhanaan saya memilih untuk menyebutkan setContentOffset. Itu adalah poin bagus tentang reloadData yang dipanggil beberapa kali dapat memicu perhitungan yang mahal. Sayang sekali tidak ada reloadData dengan metode penangan penyelesaian.   -  person Reid Main    schedule 01.10.2013
comment
Hubungi layoutIfNeeded di UICollectionView sebelum mengakses contentSize-nya   -  person onmyway133    schedule 05.03.2020


Jawaban (6)


Untuk mendapatkan ukuran konten setelah memuat ulang, coba panggil collectionViewContentSize objek tata letak. Ini berhasil untuk saya.

person Clement T    schedule 12.02.2014
comment
Saya skeptis pada awalnya tapi hei itu berhasil juga untuk saya :) - person Zhang; 09.06.2014
comment
Sepertinya panggilan ke reloadData tidak langsung memicu tampilan koleksi untuk mengubah properti rendernya, tetapi seperti yang disarankan Clement, Anda dapat langsung membuka objek layout untuk mendapatkan informasi ini dengan segera. - person Reid Main; 23.05.2015
comment
SWIFT 3: biarkan contentSize = self.myCollectionView.collectionViewLayout.collectionViewContentSize - person drpawelo; 23.10.2017
comment
Ini memecahkan masalah saya juga :) memanggil melakukan pembaruan batch untuk mendapatkan tinggi konten dan mengalami kerusakan tetapi masalah utama saya adalah tinggi konten belum ada. Tapi tata letak saya memiliki ketinggian yang tepat. - person Andrew Edwards; 06.12.2018

Ini berhasil untuk saya:

[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView.collectionViewLayout prepareLayout];
person almas    schedule 20.04.2015
comment
Berfungsi untuk saya dengan kombinasi layoutIfNeeded itu. - person Kiran Jasvanee; 13.05.2016
comment
Ini berhasil untuk saya. Saya mendapatkan bug ini dari sel ukuran sendiri, bahwa contentSize tidak diperbarui setelah reloadData di iOS 10 dan sebelumnya. Di iOS 11 saya tidak perlu melakukan ini. - person X.Y.; 12.03.2018
comment
@x.y, bagaimana cara memperbaikinya di iOS 11? Apakah berhasil? - person LinusGeffarth; 27.06.2018

Sunting: Saya baru saja menguji ini dan memang, ketika data berubah, solusi asli saya akan macet dengan yang berikut:

"Pembaruan tidak valid: jumlah item yang tidak valid di bagian 0. Jumlah item yang terdapat di bagian yang ada setelah pembaruan (7) harus sama dengan jumlah item yang terdapat di bagian tersebut sebelum pembaruan (100), ditambah atau dikurangi jumlah item yang dimasukkan atau dihapus dari bagian itu (0 dimasukkan, 0 dihapus) dan ditambah atau dikurangi jumlah item yang dipindahkan ke dalam atau keluar dari bagian itu (0 dipindahkan masuk, 0 dipindahkan keluar)."

Cara yang tepat untuk menangani hal ini adalah dengan menghitung penyisipan, penghapusan, dan pemindahan setiap kali sumber data berubah dan menggunakan performBatchUpdates di sekitarnya saat terjadi perubahan. Misalnya, jika dua item ditambahkan ke akhir array yang merupakan sumber data, ini akan menjadi kodenya:

NSArray *indexPaths = @[indexPath1, indexPath2];
[self.collectionView performBatchUpdates:^() 
{
    [self.collectionView insertItemsAtIndexPaths:indexPaths];
} completion:^(BOOL finished) {
    // TODO: Whatever it is you want to do now that you know the contentSize.
}];

Di bawah ini adalah solusi yang telah diedit dari jawaban asli saya yang disediakan oleh Kernix yang menurut saya tidak dijamin akan berhasil.

Coba performBatchUpdates:completion: di UICollectionView. Anda harus memiliki akses ke properti yang diperbarui di blok penyelesaian. Ini akan terlihat seperti ini:

[self.collectionView reloadData];
[self.collectionView performBatchUpdates:^() 
{

} completion:^(BOOL finished) {
    // TODO: Whatever it is you want to do now that you know the contentSize.
}];
person brodney    schedule 12.10.2013
comment
Itu memang terlihat seperti solusi yang solid. Perubahan akan menganimasikan hal yang tidak saya inginkan, tetapi saya yakin pasti ada cara untuk mematikan animasi tersebut. - person Reid Main; 15.10.2013
comment
Anda dapat mengganti atribut tata letak awal di subkelas tata letak menggunakan initialLayoutAttributesForAppearingItemAtIndexPath:. Atur frame disana menjadi frame sel yang berubah. - person brodney; 15.10.2013
comment
Saya cukup yakin ini akan macet, Anda harus mengeluarkan reloadData dari blok performBatch. - person Aurelien Porte; 29.01.2014

Setelah menelepon

[self.collectionView reloadData];

Gunakan ini:

[self.collectionView setNeedsLayout];
[self.collectionView layoutIfNeeded];

maka kamu bisa mendapatkan contentSize yang asli

person Ben Shabat    schedule 02.06.2021

Panggil prepLayout terlebih dahulu, dan kemudian Anda akan mendapatkan contentSize yang benar:

[self.collectionView.collectionViewLayout prepareLayout];
person Robert Mao    schedule 16.05.2014
comment
Salah. Implementasi default tidak melakukan apa pun, seperti yang dinyatakan oleh Apple: developer.apple.com/library/prerelease/ios/documentation/UIKit/ - person Can Poyrazoğlu; 08.06.2015
comment
setuju dengan @CanPoyrazoğlu - person Max MacLeod; 17.08.2015
comment
Ini menarik - Saya punya kasus di mana panggilan invalidateLayout diikuti dengan reloadData tidak akan memicu panggilan sizeForItem:. Namun, memanggil prepLayout secara langsung antara invalidate dan reload menyebabkan ukuran dihitung ulang selama memuat ulang seperti yang saya harapkan... Dokumen mengatakan implementasinya kosong, tapi saya tidak yakin saya sepenuhnya percaya ini... - person tyler; 14.09.2016
comment
@CanPoyrazoğlu dokumen mengatakan definisi metode kosong untuk UICollectionViewLayout. Dalam banyak kasus, Anda akan menggunakan subkelas default, UICollectionViewFlowLayout, yang tampaknya tidak memiliki implementasi kosong, dan mengapa sebagian besar berfungsi. - person Jordan Smith; 28.04.2017

Dalam kasus saya, saya memiliki kelas tata letak khusus yang mewarisi UICollectionViewFlowLayout dan mengaturnya hanya di Pembuat Antarmuka. Saya tidak sengaja menghapus kelas dari proyek tetapi Xcode tidak memberi saya kesalahan apa pun, dan contentSize tampilan koleksi selalu kembali nol.

Saya mengembalikan kelas dan mulai berfungsi lagi.

person da1    schedule 31.10.2019