Pertanyaan UICollectionView flowLayout tidak membungkus sel dengan benar (iOS)


Saya punya UICollectionView dengan FLowLayout. Ini akan bekerja seperti yang saya harapkan sebagian besar waktu, tetapi setiap sekarang dan kemudian salah satu sel tidak membungkus dengan benar. Misalnya, sel yang seharusnya berada di "kolom" pertama dari baris ketiga jika benar-benar tertinggal di baris kedua dan hanya ada ruang kosong di mana seharusnya (lihat diagram di bawah). Yang dapat Anda lihat dari sel rouge ini adalah sisi kiri (sisanya dipotong) dan tempat yang seharusnya kosong.

Ini tidak terjadi secara konsisten; tidak selalu baris yang sama. Setelah itu terjadi, saya dapat menggulir ke atas dan kemudian kembali dan sel akan memperbaiki dirinya sendiri. Atau, ketika saya menekan sel (yang membawa saya ke tampilan berikutnya melalui push) dan kemudian pop kembali, saya akan melihat sel di posisi yang salah dan kemudian akan melompat ke posisi yang benar.

Kecepatan gulir tampaknya mempermudah untuk mereproduksi masalah. Ketika saya menggulir perlahan, saya masih bisa melihat sel di posisi yang salah setiap sekarang dan kemudian, tetapi kemudian akan langsung melompat ke posisi yang benar.

Masalahnya dimulai ketika saya menambahkan insets bagian. Sebelumnya, saya memiliki sel hampir rata dengan batas koleksi (sedikit, atau tidak ada insets) dan saya tidak melihat masalah. Tapi ini berarti pandangan koleksi kanan dan kiri kosong. Yaitu, tidak bisa gulir. Juga, bilah gulir tidak menyiram ke kanan.

Saya dapat membuat masalah terjadi pada Simulator dan iPad 3.

Saya kira masalahnya terjadi karena inset bagian kiri dan kanan ... Tetapi jika nilainya salah, maka saya akan mengharapkan perilaku itu konsisten. Saya ingin tahu apakah ini mungkin bug dengan Apple? Atau mungkin ini karena membangun insets atau yang serupa ...

Solusi / solusi apa pun dihargai.

Illustration of problem and settings


Mengikuti: Saya telah menggunakan jawaban ini di bawah ini oleh Nick untuk lebih 6 bulan  12 bulan 2 tahun sekarang tanpa masalah (seandainya orang bertanya-tanya apakah ada lubang dalam jawaban itu - saya belum menemukannya). Nick yang dilakukan dengan baik.


75
2017-10-17 04:18


asal


Jawaban:


Ada bug dalam implementasi UICollectionViewFlowLayout dari layoutAttributesForElementsInRect yang menyebabkannya mengembalikan DUA objek atribut untuk sel tunggal dalam kasus-kasus tertentu yang melibatkan insets bagian. Salah satu objek atribut yang dikembalikan tidak valid (di luar batas tampilan koleksi) dan yang lainnya valid. Di bawah ini adalah subkelas UICollectionViewFlowLayout yang memperbaiki masalah dengan mengecualikan sel di luar batas tampilan koleksi.

// NDCollectionViewFlowLayout.h
@interface NDCollectionViewFlowLayout : UICollectionViewFlowLayout
@end

// NDCollectionViewFlowLayout.m
#import "NDCollectionViewFlowLayout.h"
@implementation NDCollectionViewFlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
  NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
  NSMutableArray *newAttributes = [NSMutableArray arrayWithCapacity:attributes.count];
  for (UICollectionViewLayoutAttributes *attribute in attributes) {
    if ((attribute.frame.origin.x + attribute.frame.size.width <= self.collectionViewContentSize.width) &&
        (attribute.frame.origin.y + attribute.frame.size.height <= self.collectionViewContentSize.height)) {
      [newAttributes addObject:attribute];
    }
  }
  return newAttributes;
}
@end

Lihat ini.

Jawaban lain menyarankan untuk mengembalikan YA dari shouldInvalidateLayoutForBoundsChange, tetapi ini menyebabkan rekomputasi yang tidak perlu dan bahkan tidak sepenuhnya menyelesaikan masalah.

Solusi saya benar-benar menyelesaikan bug dan seharusnya tidak menimbulkan masalah ketika Apple memperbaiki akar permasalahannya.


91
2017-11-14 23:55



Saya menemukan masalah serupa dalam aplikasi iPhone saya. Pencarian forum dev Apple membawa saya solusi yang cocok ini yang bekerja dalam kasus saya dan mungkin juga dalam kasus Anda:

Subkelas UICollectionViewFlowLayout dan menimpa shouldInvalidateLayoutForBoundsChange mengembalikan YES.

//.h
@interface MainLayout : UICollectionViewFlowLayout
@end

dan

//.m
#import "MainLayout.h"
@implementation MainLayout
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
@end

7
2017-10-31 20:06



Masukkan ini ke viewController yang memiliki tampilan koleksi

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

7
2018-06-03 08:48



Jawaban Cepat Jawaban Nick Snyder:

class NDCollectionViewFlowLayout : UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)
        let contentSize = collectionViewContentSize
        return attributes?.filter { $0.frame.maxX <= contentSize.width && $0.frame.maxY < contentSize.height }
    }
}

7
2017-07-03 12:06



Saya punya masalah ini juga untuk layout gridview dasar dengan insets untuk margin. Debugging terbatas yang saya lakukan untuk saat ini adalah implementasi - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect di subclass UICollectionViewFlowLayout saya dan dengan mencatat kembali apa yang dihasilkan oleh kelas super, yang dengan jelas menunjukkan masalah.

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *attrsList = [super layoutAttributesForElementsInRect:rect];

    for (UICollectionViewLayoutAttributes *attrs in attrsList) {
        NSLog(@"%f %f", attrs.frame.origin.x, attrs.frame.origin.y);
    }

    return attrsList;
}

Dengan menerapkan - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath Saya juga dapat melihat bahwa tampaknya mengembalikan nilai yang salah untuk itemIndexPath.item == 30, yang merupakan faktor 10 dari jumlah sel gridview saya per baris, tidak yakin jika itu relevan.

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
    UICollectionViewLayoutAttributes *attrs = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];

    NSLog(@"initialAttrs: %f %f atIndexPath: %d", attrs.frame.origin.x, attrs.frame.origin.y, itemIndexPath.item);

    return attrs;
}

Dengan kurangnya waktu untuk lebih banyak debugging, solusi yang telah saya lakukan untuk saat ini adalah mengurangi lebar tampilan koleksi saya dengan jumlah yang sama dengan margin kiri dan kanan. Saya memiliki header yang masih membutuhkan lebar penuh jadi saya telah mengatur clipToBounds = NO pada collectionview saya dan kemudian juga menghapus inset kiri dan kanan di atasnya, sepertinya berfungsi. Untuk tampilan header untuk kemudian tetap di tempat Anda perlu menerapkan pergeseran bingkai dan ukuran dalam metode tata letak yang ditugasi dengan kembali layoutAttributes untuk tampilan header.


5
2017-10-17 09:28



Saya telah menambahkan laporan bug ke Apple. Apa yang berhasil bagi saya adalah mengatur bagian bawahInset ke nilai yang kurang dari inset teratas.


4
2017-11-08 13:26



Saya mengalami masalah cell-deplacing yang sama pada iPhone menggunakan UICollectionViewFlowLayout dan saya senang menemukan pos Anda. Saya tahu Anda mengalami masalah pada iPad, tetapi saya memposting ini karena saya pikir ini adalah masalah umum dengan UICollectionView. Jadi inilah yang saya temukan.

Saya dapat mengonfirmasi bahwa sectionInset relevan dengan masalah itu. Selain itu headerReferenceSize juga memiliki pengaruh apakah sebuah sel mengalami deplaced atau tidak. (Ini masuk akal karena diperlukan untuk menghitung asal.)

Sayangnya, bahkan ukuran layar yang berbeda harus diperhitungkan. Ketika bermain-main dengan nilai-nilai untuk dua properti ini, saya mengalami bahwa konfigurasi tertentu bekerja baik pada keduanya (3,5 "dan 4"), pada tidak ada, atau hanya pada salah satu ukuran layar. Biasanya tidak ada satupun dari mereka. (Ini juga masuk akal, karena batas - batas UICollectionView perubahan, oleh karena itu saya tidak mengalami disparitas antara retina dan non-retina.)

Saya akhirnya mengatur sectionInset dan headerReferenceSize tergantung pada ukuran layar. Saya mencoba sekitar 50 kombinasi sampai saya menemukan nilai-nilai di mana masalah tidak terjadi lagi dan tata letak secara visual dapat diterima. Sangat sulit menemukan nilai yang bekerja pada kedua ukuran layar.

Jadi meringkas, saya hanya dapat merekomendasikan Anda untuk bermain-main dengan nilai-nilai, periksa ini pada ukuran layar yang berbeda dan berharap Apple akan memperbaiki masalah ini.


3
2017-10-29 17:50