Masalah autoresizing bingkai UICollectionViewCell contentView di sel prototipe Storyboard (Xcode 6, iOS 8 SDK) terjadi ketika hanya berjalan di iOS 7

158

Saya menggunakan Xcode 6 Beta 3, iOS 8 SDK. Bangun Target iOS 7.0 menggunakan Swift. Silakan merujuk masalah saya selangkah demi selangkah dengan tangkapan layar di bawah ini.

Saya memiliki UICollectionView di Storyboard. 1 Prototipe UICollectionViewCell yang berisi 1 label di tengah (tidak ada aturan autoresizing). Latar belakang ungu adalah untuk menandai contentView yang dihasilkan dalam runtime oleh Cell kurasa. Pandangan itu akan diubah ukurannya dengan benar berdasarkan pada UICollectionViewLayoutDelegate saya pada akhirnya, tetapi tidak pada iOS 7. Perhatikan bahwa saya menggunakan Xcode 6 dan masalahnya hanya terjadi pada iOS 7.

Ketika saya membangun aplikasi di iOS 8. Semuanya baik-baik saja.

Catatan: Ungu adalah contentView , Biru adalah UIButton saya dengan sudut bulat.

http://i.stack.imgur.com/uDNDY.png

Namun, pada iOS 7, semua subViews di dalam Cell tiba-tiba menyusut ke bingkai (0,0,50,50) dan tidak pernah lagi mematuhi aturan Autoresizing saya lagi.

http://i.stack.imgur.com/lOZH9.png

Saya berasumsi ini adalah bug di iOS 8 SDK atau Swift atau mungkin Xcode?


Pembaruan 1: Masalah ini masih ada di Xcode 6.0.1 resmi! Pekerjaan terbaik di sekitar adalah seperti apa yang disarankan KoCMoHaBTa di bawah ini dengan mengatur bingkai di cellForItem sel (Anda harus subkelas sel Anda). Ternyata ini adalah ketidakcocokan antara iOS 8 SDK dan iOS 7 (lihat jawaban ecotax di bawah yang dikutip dari Apple).

Pembaruan 2: Tempel kode ini di awal cellForItem Anda dan semuanya akan baik-baik saja:

/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/
thkeen
sumber
1
Saya menemukan bahwa masalah ini masih ada di Xcode 6 Beta 5. Adakah yang pernah mengalami ini juga?
thkeen
2
Saya sedang berjuang dengan ini sekarang di proyek saya. iOS 7 dan iOS 8 dibangun menggunakan Xcode 5 terlihat bagus. iOS 8 dibangun menggunakan Xcode 6 beta 6 terlihat baik-baik saja. iOS 7 dibangun menggunakan Xcode 6 beta 6 mengalami masalah yang Anda gambarkan. Menggunakan Reveal, saya dapat melihat bahwa UICollectionViewCell saya telah berukuran benar. Tetapi contentView sel tidak diubah ukurannya, meskipun itu adalah induknya, UICollectionViewCell telah mengaktifkan Subviews Autoresize dihidupkan. Ukuran contentView diatur ke apa yang dimiliki storyboard. Saya tidak menggunakan autolayout dalam proyek ini. Proyek saya sepenuhnya objektif-c.
Del Brown
2
Saya hanya ingin menambahkan bahwa masalah ini masih ada pada Xcode 6 / iOS 8 GM seed. @ DanielPlamann menjawab untuk memaksa contentViewmengubah ukuran dengan sel berfungsi dengan baik untuk menyelesaikan masalah. Saya kira di iOS 8 Apple mengubah sesuatu tentang cara tampilan konten sel ditangani ketika dibuat di Interface Builder (yang masih sedikit kotak hitam). Tetapi fakta bahwa itu mengubah perilaku ketika menargetkan iOS 7 pasti bug.
Stuart
Sama di sini dengan Xcode 6 GM, tata letak otomatis dan sel berbasis pena. Saya memperbaikinya dengan menyematkan contentViewtepi ke tepi sel.
sergiou87
3
Saya mengunduh xcode 6.1 tetapi masih melihat masalah yang sama di simulator.
Haitao Li

Jawaban:

169

contentView rusak. Itu juga bisa diperbaiki di awakeFromNib

ObjC:

- (void)awakeFromNib {

    [super awakeFromNib];

    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}

Swift3:

override func awakeFromNib() {
    super.awakeFromNib()

    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
Igor Palaguta
sumber
3
Melakukan trik tanpa self.contentView.frame = self.bounds; Pikirkan saya membutuhkannya? Bagaimanapun, terima kasih!
Michal Shatz
Halo Michal, setuju dengan Anda. Tapi untuk 100% yang pasti, bagaimana cara kerjanya di iOS berikutnya lebih baik untuk menambahkannya saya pikir.
Igor Palaguta
5
Saya suka jawaban ini tetapi Anda perlu memanggil [super awakeFromNib]; Juga, saya sedang berpikir tentang menambahkan cek untuk iOS 7.1 dan lebih sedikit, karena saya tidak yakin bagaimana menambahkan topeng pengubah ukuran ini memengaruhi perilaku default di iOS8.
GingerBreadMane
Jika Anda tidak menggunakan nibs atau storyboard, ini juga berfungsi jika Anda memasukkannya ke applyLayoutAttributes:
cetcet
Ini tidak berhasil untuk saya. Saya tidak menggunakan Autolayout !! Ada bantuan?
thatzprem
61

Saya mengalami masalah yang sama dan meminta bantuan Apple DTS. Jawaban mereka adalah:

Di iOS 7, tampilan konten sel mengukur dirinya sendiri melalui masker yang diautorisasi. Di iOS 8, ini diubah, sel-sel berhenti menggunakan topeng autoresizing dan mulai mengukur tampilan konten di layoutSubviews. Jika nib dikodekan di iOS 8 dan kemudian mendekodekannya di iOS 7, Anda akan memiliki tampilan konten tanpa masker autoresizing dan tidak ada cara lain untuk mengukur ukuran itu sendiri. Jadi, jika Anda pernah mengubah bingkai sel, tampilan konten tidak akan mengikuti.

Aplikasi yang sedang digunakan kembali ke iOS 7 harus mengatasi ini dengan mengukur sendiri ukuran konten, menambahkan masker autoresizing, atau menambahkan kendala.

Saya kira ini berarti itu bukan bug di XCode 6, tetapi ketidakcocokan antara iOS 8 SDK dan iOS 7 SDK, yang akan mengenai Anda jika Anda memutakhirkan ke Xcode 6, karena ia akan secara otomatis mulai menggunakan iOS 8 SDK.

Seperti yang saya komentari sebelumnya, solusi Daniel Plamann menjelaskan pekerjaan untuk saya. Yang dijelaskan oleh Igor Palaguta dan KoCMoHaBTa terlihat lebih sederhana, dan tampaknya masuk akal memberikan jawaban Apple DTS, jadi saya akan mencobanya nanti.

ecotax
sumber
Ini menarik, tapi saya masih berharap mereka memperbaikinya. Perilaku ini hanya diperkenalkan di Xcode 6 GM yang menambahkan dukungan iPhone 6. Itu bekerja dengan baik di beta sebelumnya. Saya bahkan membawa proyek kembali ke beta sebelumnya setelah saya perhatikan dan itu berfungsi seperti yang diharapkan. Saya harap semua orang melaporkan bug dengan Apple tentang masalah ini.
arton
@arton Saya mengajukan laporan bug pada hari yang sama saya meminta bantuan DTS. Itu ditutup sebagai duplikat dari 18312246. Tidak yakin seberapa banyak ini membantu.
ecotax
Saya telah menggunakan pekerjaan sekitar dengan mengukur ContentView dan bekerja untuk saya. - (CGSize) collectionViewContentSize {return CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Jesús Hurtado
60

Saya mengalami masalah yang sama dan berharap Apple akan memperbaikinya dengan versi Xcode berikutnya. Sementara itu saya menggunakan solusi. Di UICollectionViewCellsubkelas saya, saya baru saja mengganti layoutSubviewsdan mengubah ukuran contentView secara manual jika ukurannya berbeda dari collectionViewCellukuran.

- (void)layoutSubviews
{
  [super layoutSubviews];

  BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);

  if( !contentViewIsAutoresized) {
    CGRect contentViewFrame = self.contentView.frame;
    contentViewFrame.size = self.frame.size;
    self.contentView.frame = contentViewFrame;
  }
}
Daniel Plamann
sumber
Ya saya menggunakan pekerjaan serupa di sekitar tetapi saya melakukannya di cellForItem / cellForRow yang juga berfungsi.
thkeen
1
Ini solusi terbaik. Menambahkan ini di cellForItem / cellForRow akan menyebabkan artefak aneh jika Anda memutar perangkat dan perubahan ukuran sel.
spybart
Hai! Saya mendapat masalah dengan kode ini. Untuk pertama kalinya, contentViewIsAutoresized boolean akan benar jika dimuat dari storyboard atau sel prototipe. Hanya ketika Anda melakukan reloadData, yang kedua itu akan benar. Jadi Anda tidak benar-benar perlu memeriksa ukurannya. Alih-alih hanya lakukan: self.contentView.frame = self.bounds;
thkeen
Dikonfirmasi: kita harus melakukannya di cellForItem atau cellForRow karena layoutSubviews hanya dipanggil setelah sel dikembalikan. Apa pun yang Anda lakukan sebelumnya, hal-hal seperti menggambar akan dihitung secara salah.
thkeen
1
Hm, mungkin lebih baik ditempatkan [cell layoutIfNeeded];di cellForItem atau cellForRow?
wtorsi
38

Solusi lain adalah dengan mengatur ukuran contentView dan masker autoresizing -collectionView:cellForItemAtIndexPath:seperti berikut:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

     static NSString *cellID = @"CellID";

     UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

     // Set contentView's frame and autoresizingMask
     cell.contentView.frame = cell.bounds;
     cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

     // Your custom code goes here

     return cell;
}

Ini berfungsi dengan Tata Letak Otomatis juga, karena topeng pengubah ukuran otomatis diterjemahkan ke kendala.

KoCMoHaBTa
sumber
2
Ini adalah solusi yang unggul karena bekerja dengan semua jenis sel tanpa subklas dan tidak memerlukan perubahan di banyak tempat ketika Anda menggunakan lebih dari satu jenis sel dalam tampilan koleksi Anda.
nacross
6

Dalam Xcode 6.0.1 contentView untuk UICollectionViewCell rusak untuk perangkat iOS7. Itu juga dapat diperbaiki dengan menambahkan batasan yang tepat untuk UICollectionViewCell dan kontennya di awakeFromNib atau metode init.

        UIView *cellContentView = self.contentView;
        cellContentView.translatesAutoresizingMaskIntoConstraints = NO;

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
SerJ_G
sumber
Hanya ini yang berfungsi sekarang, Anda harus melakukannya hanya untuk iOS 8, dan saya harus memanggilnya setelah setiap dequeue! Tapi ini juga bukan solusi ideal karena banyak pilihan tidak berfungsi sebagaimana mestinya secara visual ...
Renetik
Yang terbaik adalah benar-benar menggunakan autolayout, dengan bug ini Anda menghemat waktu ironisnya dengan menggunakan acara autolayout dalam kasus sederhana ..
Renetik
Topeng tidak berfungsi untukku; Namun pengaturan kendala berhasil!
entropid
4

Ini tidak akan berfungsi dengan benar tanpa ada solusi lain yang disebutkan karena bug di Xcode 6 GM dengan cara Xcode mengkompilasi file xib ke dalam format nib. Walaupun saya tidak bisa mengatakan untuk kepastian 100% bahwa ini terkait dengan Xcode dan tidak ada hubungannya dengan runtime, saya sangat yakin - inilah cara saya dapat menunjukkannya:

  1. Bangun + Jalankan aplikasi dalam Xcode 5.1.
  2. Buka direktori aplikasi simulator dan salin file .nib yang dikompilasi untuk xib yang Anda alami masalah.
  3. Bangun + Jalankan aplikasi dalam Xcode 6 GM.
  4. Hentikan aplikasi.
  5. Ganti file .nib di folder simulator aplikasi yang baru dibangun dengan file .nib yang dibuat menggunakan Xcode 5.1
  6. Luncurkan ulang aplikasi dari simulator, BUKAN dari Xcode.
  7. Sel Anda dimuat dari .nib itu harus berfungsi seperti yang diharapkan.

Saya harap semua orang yang membaca pertanyaan ini akan mengajukan Radar dengan Apple. Ini adalah masalah BESAR dan perlu ditangani sebelum rilis Xcode akhir.

Sunting: Sehubungan dengan posting ecotax , saya hanya ingin memperbarui ini untuk mengatakan bahwa sekarang dikonfirmasi perbedaan perilaku antara membangun di iOS 8 vs iOS 7, tetapi bukan bug. Peretasan saya memperbaiki masalah ini karena membangun di iOS 7 menambahkan masker autoresizing ke tampilan konten yang diperlukan untuk membuat ini berfungsi, yang tidak lagi ditambahkan oleh Apple.

Acey
sumber
Saya tidak yakin apakah mereka secara hukum dapat merilis xCode yang berbeda dari versi GM
Mabedan
Haha akankah mereka masuk penjara? = P Tetapi dalam keseriusan semua, tentu mereka bisa. Mereka memiliki beberapa versi GM untuk Mavericks jika Anda tidak ingat. Itu tidak terlalu umum tetapi itu bisa terjadi.
Acey
4

Jawaban di postingan ini berfungsi, apa yang saya tidak pernah mengerti adalah mengapa ini berhasil.

Pertama, ada dua "aturan":

  1. Untuk tampilan yang dibuat secara terprogram (Kel. [UIView new]), Properti translatesAutoresizingMaskIntoConstraintsdisetel keYES
  2. Tampilan yang dibuat di pembangun antarmuka, dengan AutoLayout diaktifkan, properti akan translatesAutoresizingMaskIntoConstraintsdisetel keNO

Aturan kedua tampaknya tidak berlaku untuk tampilan tingkat atas yang Anda tidak mendefinisikan batasannya. (Mis. Tampilan konten)

Saat melihat sel Storyboard, perhatikan bahwa selnya tidak contentViewterbuka. Kami tidak "mengendalikan" contentView, Apple.

Selami lebih dalam kode sumber storyboard dan lihat bagaimana contentViewsel didefinisikan:

<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">

Sekarang subview sel (perhatikan translatesAutoresizingMaskIntoConstraints="NO"):

<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">

The contentViewtidak memiliki itu translatesAutoresizingMaskIntoConstraintsdiatur untuk NO. Plus itu tidak memiliki definisi tata letak, mungkin karena apa yang dikatakan @ecotax .

Jika kita melihat ke dalam contentView, itu memang memiliki topeng autoresizing, tetapi tidak ada definisi untuk itu: <autoresizingMask key="autoresizingMask"/>

Jadi ada dua kesimpulan:

  1. contentView translatesAutoresizingMaskIntoConstraintsdiatur ke YES.
  2. contentView tidak memiliki definisi tata letak.

Ini membawa kita ke dua solusi yang telah dibicarakan.

Anda dapat mengatur masker autoresizing secara manual di awakeFromNib:

self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Atau Anda dapat mengatur contentView translatesAutoresizingMaskIntoConstraintsuntuk NOmasuk awakeFromNibdan mendefinisikan kendala di - (void)updateConstraints.

kgaidis
sumber
4

Ini adalah versi Swift dari jawaban Igor yang diterima dan terima kasih atas jawaban Anda yang baik.

Pertama-tama, ambil UICollectionViewCellSubclass Anda dan rekatkan kode berikut karena berada di dalam kelas.

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}

Omong-omong saya menggunakan Xcode 7.3.1 dan Swift 2.3. Solusi diuji pada iOS 9.3 yang berfungsi dengan sempurna.

Terima kasih, semoga ini membantu.

dalam penyelesaian
sumber
maaf untuk bahasa Inggris saya yang buruk, maksud saya "berfungsi seperti pesona" :)
Aznix
@Aznix Tidak masalah .. :)
onCompletion
2

Di cepat, tempatkan kode berikut dalam subkelas sel tampilan kumpulan:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
Ian
sumber
Ini adalah jawaban yang saya cari. Saya dulu mengesampingkan setBounds di Objective-C untuk memperbaiki masalah ini (tidak yakin bagaimana menulisnya di Swift). Terima kasih :)
Matius Cawley
1

Saya telah menemukan bahwa ada juga masalah dengan contentViewukuran di iOS 8. Itu cenderung ditata sangat terlambat dalam siklus, yang dapat menyebabkan konflik kendala sementara. Untuk mengatasi ini, saya menambahkan metode berikut dalam kategori UICollectionViewCell:

- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        self.contentView.frame = self.bounds;
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    } else {
        [self layoutIfNeeded];
    }
#endif
#endif
}

Metode ini harus dipanggil setelah mengeluarkan sel.

Phatmann
sumber
Akan ideal jika metode ini dipanggil secara otomatis. Saya tidak suka itu, tetapi ini bisa dilakukan dengan swizzling dequeueReusableCellWithReuseIdentifier:forIndexPath:.
phatmann
Apple tampaknya telah memperbaiki masalah ini dalam versi 8.1 dari iOS SDK di Xcode 6.1 GM (Build 6A1042b). Oleh karena itu saya telah memperbarui kode di atas agar tidak berjalan ketika menggunakan 8.1 SDK. Setelah semua tim Anda dipindahkan ke Xcode 6.1, Anda dapat menghapus hack ini seluruhnya.
phatmann
1

Saya tetap melakukan ini:

override func layoutSubviews() {
   contentView.superview?.frame = bounds
   super.layoutSubviews()
}

lihat di sini

Serluca
sumber
-1

Pastikan Anda mencentang kotak "Autoresize subviews" di nib untuk sel tampilan koleksi. Ini akan berfungsi dengan baik di iOS 8 dan iOS 7.

Deepak GM
sumber
Tidak, tidak. Ini adalah sel prototipe tertanam.
thkeen