Das Problem der automatischen Größenänderung des Frames von UICollectionViewCell contentView in der Storyboard-Prototypzelle (Xcode 6, iOS 8 SDK) tritt nur unter iOS 7 auf

158

Ich verwende Xcode 6 Beta 3, iOS 8 SDK. Erstellen Sie das Ziel iOS 7.0 mit Swift. Bitte beziehen Sie sich Schritt für Schritt auf mein Problem mit den folgenden Screenshots.

Ich habe eine UICollectionView im Storyboard. 1 Prototyp UICollectionViewCell, der 1 Label in der Mitte enthält (keine automatische Größenregel). Lila Hintergrund sollte eine Inhaltsansicht markieren, die zur Laufzeit von der Zelle generiert wird, denke ich. Die Größe dieser Ansicht wird basierend auf meinem UICollectionViewLayoutDelegate eventuell ordnungsgemäß geändert, jedoch nicht unter iOS 7. Beachten Sie, dass ich Xcode 6 verwende und das Problem nur unter iOS 7 auftritt.

Wenn ich die App auf iOS 8 baue. Alles ist in Ordnung.

Hinweis: Lila ist die Inhaltsansicht , Blau ist mein UIButton mit abgerundeter Ecke.

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

Unter iOS 7 schrumpfen jedoch alle Unteransichten in der Zelle plötzlich auf den Rahmen von (0,0,50,50) und entsprechen nie mehr meiner Autoresizing-Regel.

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

Ich nehme an, dies ist ein Fehler in iOS 8 SDK oder Swift oder vielleicht Xcode?


Update 1: Dieses Problem besteht weiterhin im offiziellen Xcode 6.0.1! Die beste Lösung ist die, die KoCMoHaBTa unten vorgeschlagen hat, indem der Frame in cellForItem der Zelle festgelegt wird (Sie müssen Ihre Zelle jedoch in Unterklassen unterteilen). Es stellte sich heraus, dass dies eine Inkompatibilität zwischen iOS 8 SDK und iOS 7 ist (siehe die unten von Apple zitierte Antwort von ecotax).

Update 2: Fügen Sie diesen Code am Anfang Ihres cellForItem ein und alles sollte in Ordnung sein:

/** 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
quelle
1
Ich habe herausgefunden, dass dieses Problem in Xcode 6 Beta 5 immer noch besteht. Hat das auch jemand erlebt?
Thkeen
2
Ich habe gerade in meinem Projekt damit zu kämpfen. iOS 7 und iOS 8, die mit Xcode 5 erstellt wurden, sehen gut aus. iOS 8, das mit Xcode 6 Beta 6 erstellt wurde, sieht gut aus. iOS 7, das mit Xcode 6 Beta 6 erstellt wurde, hat das von Ihnen beschriebene Problem. Mit Reveal kann ich feststellen, dass meine UICollectionViewCell die richtige Größe hat. Die Größe der Inhaltsansicht der Zelle wurde jedoch nicht geändert, obwohl sie übergeordnet ist. In der UICollectionViewCell sind die Unteransichten für die automatische Größe aktiviert. Die Größe der Inhaltsansicht wird auf die Größe des Storyboards festgelegt. Ich verwende in diesem Projekt kein Autolayout. Mein Projekt ist völlig objektiv-c.
Del Brown
2
Ich wollte nur hinzufügen, dass dieses Problem ab Xcode 6 / iOS 8 GM-Seed immer noch besteht. @ DanielPlamanns Antwort, die contentViewGrößenänderung mit der Zelle zu erzwingen , funktioniert gut, um das Problem zu umgehen. Ich vermute, dass Apple in iOS 8 etwas an der Art und Weise geändert hat, wie Ansichten von Zelleninhalten behandelt werden, wenn sie in Interface Builder erstellt werden (was ohnehin immer noch eine Art Black Box ist). Aber die Tatsache, dass es das Verhalten beim Targeting von iOS 7 ändert, ist sicherlich ein Fehler.
Stuart
Gleiches gilt hier für Xcode 6 GM, automatisches Layout und eine nibbasierte Zelle. Ich repariere es, indem ich die contentViewKanten an die Zellenränder hefte.
Sergiou87
3
Ich habe xcode 6.1 heruntergeladen, sehe aber immer noch das gleiche Problem im Simulator.
Haitao Li

Antworten:

169

contentView ist kaputt. Es kann auch in awakeFromNib behoben werden

Objekt:

- (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
quelle
3
Hat den Trick ohne self.contentView.frame = self.bounds gemacht; Glaubst du, ich brauche es? Danke trotzdem!
Michal Shatz
Hallo Michal, stimme dir zu. Aber um 100% sicher zu sein, wie wird es in den nächsten iOSes besser funktionieren, es hinzuzufügen, denke ich.
Igor Palaguta
5
Ich mag diese Antwort, aber Sie müssen [super awakeFromNib] aufrufen; Außerdem denke ich darüber nach, eine Prüfung für iOS 7.1 und weniger hinzuzufügen, da ich nicht sicher bin, wie sich das Hinzufügen dieser Größenänderungsmasken auf das Standardverhalten unter iOS8 auswirkt.
GingerBreadMane
Wenn Sie keine Schreibfedern oder Storyboards verwenden, funktioniert dies auch, wenn Sie es in applyLayoutAttributes einfügen:
cetcet
Das funktioniert bei mir nicht. Ich benutze kein Autolayout !! Hilfe?
Thatzprem
61

Ich bin auf dasselbe Problem gestoßen und habe Apple DTS um Hilfe gebeten. Ihre Antwort war:

In iOS 7 haben sich die Inhaltsansichten der Zellen über automatische Größenänderungsmasken angepasst. In iOS 8 wurde dies geändert, Zellen verwendeten die Masken für die automatische Größenänderung nicht mehr und begannen, die Größe der Inhaltsansicht in layoutSubviews zu ändern. Wenn eine Schreibfeder in iOS 8 codiert und dann in iOS 7 dekodiert wird, haben Sie eine Inhaltsansicht ohne Autoresizing-Maske und ohne andere Mittel, um die Größe selbst zu bestimmen. Wenn Sie also jemals den Rahmen der Zelle ändern, folgt die Inhaltsansicht nicht.

Apps, die wieder auf iOS 7 bereitgestellt werden, müssen dies umgehen, indem sie die Größe der Inhaltsansicht selbst ändern, Masken für die automatische Größenänderung hinzufügen oder Einschränkungen hinzufügen.

Ich denke, dies bedeutet, dass es sich nicht um einen Fehler in XCode 6 handelt, sondern um eine Inkompatibilität zwischen dem iOS 8 SDK und dem iOS 7 SDK, die Sie beim Upgrade auf Xcode 6 treffen wird, da das iOS 8 SDK automatisch verwendet wird.

Wie ich bereits sagte, funktioniert die von Daniel Plamann beschriebene Problemumgehung für mich. Die von Igor Palaguta und KoCMoHaBTa beschriebenen sehen jedoch einfacher aus und scheinen sinnvoll zu sein, wenn man die Antwort von Apple DTS gibt. Deshalb werde ich sie später versuchen.

Ökotax
quelle
Das ist interessant, aber ich hoffe immer noch, dass sie es beheben. Dieses Verhalten wurde nur in Xcode 6 GM eingeführt, das die iPhone 6-Unterstützung hinzufügte. In den früheren Betas hat es gut funktioniert. Ich habe sogar ein Projekt auf eine frühere Beta zurückgebracht, nachdem ich es bemerkt hatte und es wie erwartet funktioniert hat. Ich hoffe, dass jeder Fehler bei Apple in dieser Angelegenheit meldet.
Arton
@arton Ich habe am selben Tag, an dem ich DTS um Hilfe gebeten habe, einen Fehlerbericht eingereicht. Es wurde als Duplikat von 18312246 geschlossen. Ich bin mir nicht sicher, wie viel dies hilft.
Ecotax
Ich habe die Problemumgehung durch Dimensionierung der ContentView verwendet und arbeite für mich. - (CGSize) collectionViewContentSize {return CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Jesús Hurtado
60

Ich bin auf dasselbe Problem gestoßen und hoffe, dass Apple dies mit der nächsten Xcode-Version beheben wird. In der Zwischenzeit verwende ich eine Problemumgehung. In meiner UICollectionViewCellUnterklasse habe ich gerade layoutSubviewsdie Inhaltsansicht überschrieben und die Größe manuell geändert, falls die Größe von der Größe abweicht collectionViewCell.

- (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
quelle
Ja, ich habe eine ähnliche Arbeit verwendet, aber ich habe sie in cellForItem / cellForRow gemacht, was auch funktioniert.
Thkeen
1
Dies ist die beste Lösung. Das Hinzufügen in cellForItem / cellForRow führt zu seltsamen Artefakten, wenn Sie das Gerät drehen und sich die Zellengröße ändert.
Spybart
Hallo! Ich habe ein Problem mit diesem Code. Zum ersten Mal ist der boolesche Inhalt contentViewIsAutoresized wahr, wenn er aus dem Storyboard oder der Prototypzelle geladen wird. Nur wenn Sie reloadData ausführen, ist es in der Sekunde korrekt. Sie müssen also nicht wirklich nach der Größe suchen. Tun Sie stattdessen einfach: self.contentView.frame = self.bounds;
Thkeen
Bestätigt: Wir sollten dies in cellForItem oder cellForRow tun, da layoutSubviews erst aufgerufen wird, nachdem die Zelle zurückgegeben wurde. Alles, was Sie vorher tun, wie das Zeichnen, wird falsch berechnet.
Thkeen
1
Hm, vielleicht ist es besser, [cell layoutIfNeeded];in cellForItem oder cellForRow zu platzieren?
wtorsi
38

Eine andere Lösung besteht darin, die Größe und die automatische Größenanpassung von contentView -collectionView:cellForItemAtIndexPath:wie folgt festzulegen :

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

Dies funktioniert auch mit dem automatischen Layout, da Masken mit automatischer Größenänderung in Einschränkungen übersetzt werden.

KoCMoHaBTa
quelle
2
Dies ist eine überlegene Lösung, da sie mit jedem Zelltyp ohne Unterklasse funktioniert und keine Änderungen an mehreren Stellen erfordert, wenn Sie mehr als einen Zelltyp in Ihrer Sammlungsansicht verwenden.
Nacross
6

In Xcode 6.0.1 ist contentView für UICollectionViewCell für iOS7-Geräte fehlerhaft. Sie kann auch behoben werden, indem Sie UICollectionViewCell und dessen contentView in awakeFromNib- oder init-Methoden geeignete Einschränkungen hinzufügen.

        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
quelle
Nur das funktioniert jetzt, Sie müssen es nur für IOS 8 tun, und ich muss es nach jeder Warteschlange aufrufen! Dies ist aber auch keine ideale Lösung, da die Mehrfachauswahl visuell nicht so funktioniert, wie sie sollte ...
Renetik
Am besten ist es, Autolayout wirklich zu verwenden. Mit diesem Fehler sparen Sie ironischerweise Zeit, indem Sie das Autolayout-Ereignis in einem einfachen Fall verwenden.
Renetik
Die Maske funktioniert bei mir nicht; Das Einstellen der Einschränkungen funktioniert jedoch!
Entropid
4

Dies funktioniert ohne eine der anderen genannten Problemumgehungen aufgrund eines Fehlers in Xcode 6 GM bei der Kompilierung von XIB-Dateien im NIB-Format nicht ordnungsgemäß. Obwohl ich nicht mit 100% iger Sicherheit sagen kann, dass es sich um Xcode handelt und nichts mit Laufzeit zu tun hat, bin ich sehr zuversichtlich - so kann ich es zeigen:

  1. Erstellen + Ausführen der Anwendung in Xcode 5.1.
  2. Wechseln Sie in das Verzeichnis der Simulatoranwendung und kopieren Sie die kompilierte .nib-Datei für die xib, mit der Sie Probleme haben.
  3. Erstellen + Ausführen der Anwendung in Xcode 6 GM.
  4. Beenden Sie die Anwendung.
  5. Ersetzen Sie die .nib-Datei im Simulatorordner der neu erstellten Anwendung durch die mit Xcode 5.1 erstellte .nib-Datei
  6. Starten Sie die App über den Simulator neu, NICHT über Xcode.
  7. Ihre von dieser .nib geladene Zelle sollte wie erwartet funktionieren.

Ich hoffe, jeder, der diese Frage liest, wird ein Radar bei Apple einreichen. Dies ist ein RIESIGES Problem und muss vor der endgültigen Veröffentlichung von Xcode behoben werden.

Bearbeiten: In Anbetracht des Beitrags von ecotax wollte ich dies nur aktualisieren, um zu sagen, dass es nun bestätigte Verhaltensunterschiede zwischen dem Erstellen in iOS 8 und iOS 7 gibt, aber kein Fehler. Mein Hack hat das Problem behoben, da auf der Grundlage von iOS 7 die Inhaltsmaske der Inhaltsansicht hinzugefügt wurde, die erforderlich ist, damit dies funktioniert, was Apple nicht mehr hinzufügt.

Acey
quelle
Ich bin nicht sicher, ob sie legal einen anderen xCode als die GM-Version veröffentlichen können
Mabedan
Haha, werden sie ins Gefängnis gehen? = P Aber im Ernst, sicher können sie. Sie hatten mehrere GM-Versionen für Mavericks, wenn Sie sich nicht erinnern. Es ist nicht allzu häufig, aber es kann passieren.
Acey
4

Die Antworten in diesem Beitrag funktionieren, was ich nie verstanden habe, ist warum es funktioniert.

Erstens gibt es zwei "Regeln":

  1. Für Ansichten, die programmgesteuert erstellt wurden (z. B. [UIView new]), wird die Eigenschaft auf translatesAutoresizingMaskIntoConstraintsfestgelegtYES
  2. Für Ansichten, die im Interface Builder mit aktiviertem AutoLayout erstellt wurden, wird die Eigenschaft translatesAutoresizingMaskIntoConstraintsfestgelegtNO

Die zweite Regel scheint nicht für Ansichten der obersten Ebene zu gelten, für die Sie keine Einschränkungen definieren. (Bsp. Die Inhaltsansicht)

Beachten Sie beim Betrachten einer Storyboard-Zelle, dass die Zelle nicht contentViewfreigelegt ist. Wir "kontrollieren" nicht diecontentView , Apple ist.

Tauchen Sie tief in den Storyboard-Quellcode ein und sehen Sie, wie die contentViewZelle definiert ist:

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

Nun die Unteransichten der Zelle (beachten Sie die translatesAutoresizingMaskIntoConstraints="NO"):

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

Das contentViewhat es nicht translatesAutoresizingMaskIntoConstraintseingestellt NO. Außerdem fehlt die Layoutdefinition , möglicherweise aufgrund der Aussagen von @ecotax .

Wenn wir uns das ansehen contentView, hat es eine Autoresizing-Maske, aber keine Definition dafür: <autoresizingMask key="autoresizingMask"/>

Es gibt also zwei Schlussfolgerungen:

  1. contentView translatesAutoresizingMaskIntoConstraintsist auf eingestellt YES.
  2. contentView Es fehlt die Definition eines Layouts.

Dies führt uns zu zwei Lösungen, über die gesprochen wurde.

Sie können die Autoresizing-Masken manuell einstellen in awakeFromNib:

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

Oder Sie können das Set contentView translatesAutoresizingMaskIntoConstraintszu NOin awakeFromNibund definieren Einschränkungen in - (void)updateConstraints.

kgaidis
quelle
4

Dies ist die Swift-Version von @ Igor's Antwort, die akzeptiert wird und danke für deinen netten Antwortkameraden.

Gehen Sie zuerst zu Ihrer UICollectionViewCellUnterklasse und fügen Sie den folgenden Code ein, wie er sich in der Klasse befindet.

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

Ich benutze übrigens Xcode 7.3.1 und Swift 2.3. Die Lösung wurde unter iOS 9.3 getestet, das einwandfrei funktioniert.

Danke, hoffe das hat geholfen.

bei Fertigstellung
quelle
Entschuldigung für mein schlechtes Englisch, ich meinte "funktioniert wie ein Zauber" :)
Aznix
@ Aznix Kein Problem .. :)
onCompletion
2

Fügen Sie in Swift den folgenden Code in die Unterklasse der Sammlungsansichtszelle ein:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
Ian
quelle
Dies war die Antwort, nach der ich gesucht habe. Ich habe setBounds in Objective-C überschrieben, um dieses Problem zu beheben (ich war mir nicht sicher, wie ich es in Swift schreiben sollte). Vielen Dank :)
Matthew Cawley
1

Ich habe festgestellt, dass es auch Probleme mit der contentViewGrößenanpassung in iOS 8 gibt. Es wird in der Regel sehr spät im Zyklus angelegt, was zu vorübergehenden Einschränkungskonflikten führen kann. Um dies zu lösen, habe ich die folgende Methode in eine Kategorie von hinzugefügt 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
}

Diese Methode sollte nach dem Entfernen der Zelle aufgerufen werden.

Phatmann
quelle
Es wäre ideal, wenn diese Methode automatisch aufgerufen würde. Ich liebe es nicht, aber das könnte durch Swizzling gemacht werden dequeueReusableCellWithReuseIdentifier:forIndexPath:.
Phatmann
Apple scheint dieses Problem in Version 8.1 des iOS SDK in Xcode 6.1 GM (Build 6A1042b) behoben zu haben. Daher habe ich den obigen Code so aktualisiert, dass er bei Verwendung des 8.1 SDK nicht ausgeführt wird. Sobald Ihr Team alle auf Xcode 6.1 umgestellt hat, können Sie diesen Hack vollständig entfernen.
Phatmann
1

Ich habe das behoben:

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

siehe: hier

Serluca
quelle
-1

Stellen Sie einfach sicher, dass Sie das Kontrollkästchen "Autoresize-Unteransichten" in der Schreibfeder für diese Sammlungsansichtszelle aktivieren. Es funktioniert sowohl unter iOS 8 als auch unter iOS 7.

Deepak GM
quelle
Nein, tut es nicht. Dies ist eine eingebettete Prototypzelle.
Thkeen