Probleme mit automatischen Layoutbeschränkungen unter iOS7 in UITableViewCell

104

Ich verwende programmgesteuert automatische Layoutbeschränkungen, um meine benutzerdefinierten UITableView-Zellen zu gestalten, und definiere die Zellengrößen in korrekt tableView:heightForRowAtIndexPath:

Es funktioniert ganz gut auf iOS6 und es tut Blick fein in iOS7 auch

ABER wenn ich die App unter iOS7 starte, ist hier die Art von Nachricht, die ich in der Konsole sehe:

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2013-10-02 09:56:44.847 Vente-Exclusive[76306:a0b] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
        "<NSLayoutConstraint:0xac4c5f0 V:|-(15)-[UIImageView:0xac47f50]   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
        "<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>",
        "<NSLayoutConstraint:0xac43680 V:[UIView:0xac4d0f0(1)]>",
        "<NSLayoutConstraint:0xac436b0 V:[UIView:0xac4d0f0]-(0)-|   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>

Und tatsächlich gibt es eine der Einschränkungen in dieser Liste, die ich nicht möchte:

"<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"

und ich kann die translatesAutoresizingMaskIntoConstraintsEigenschaft von nicht contentViewauf NO setzen => es würde die gesamte Zelle durcheinander bringen.

44 ist die Standardzellenhöhe, aber ich habe meine benutzerdefinierten Höhen im Tabellenansicht-Delegaten definiert. Warum hat die Zelleninhaltsansicht diese Einschränkung? Was könnte das verursachen?

In iOS6 passiert das nicht und sowohl auf iOS6 als auch auf iOS7 sieht alles gut aus.

Mein Code ist ziemlich groß, daher werde ich ihn nicht hier veröffentlichen, aber Sie können jederzeit nach einem Pastebin fragen, wenn Sie ihn benötigen.

Um anzugeben, wie ich es bei der Zellinitialisierung mache:

  • Ich erstelle alle meine Beschriftungen, Schaltflächen usw.
  • Ich habe ihr translatesAutoresizingMaskIntoConstraintsEigentum auf NEIN gesetzt
  • Ich füge sie als Unteransichten contentViewder Zelle hinzu
  • Ich füge die Einschränkungen für die contentView

Ich bin auch sehr daran interessiert zu verstehen, warum dies nur unter iOS7 geschieht.

Alexis
quelle
Auf welche benutzerdefinierte Zellenhöhe ist eingestellt?
Mike Pollard
Meine benutzerdefinierte Zellenhöhe ist auf 90 eingestellt
Alexis
Ein Kollege hatte das gleiche Problem gestern, aber mit der Standardbreite 320 sein (für iPad - App).
Jrturton
Es gibt hier ein Beispielprojekt, das dasselbe Problem demonstriert: github.com/Alex311/TableCellWithAutoLayout Hier sind einige meiner Beobachtungen dazu: github.com/Alex311/TableCellWithAutoLayout/commit/…
smileyborg
Ich bin auf den gleichen Fehler gestoßen, aber das lag daran, dass ich die zusätzliche Prototypzelle, mit der ich die Höhe meiner benutzerdefinierten Zelle ermittelt habe, nie initialisiert habe.
Steve Moser

Antworten:

132

Ich hatte auch dieses Problem. Es scheint, dass der Frame von contentView erst aktualisiert wird, wenn er layoutSubviewsaufgerufen wird. Der Frame der Zelle wird jedoch früher aktualisiert, sodass der Frame von contentView {0, 0, 320, 44}zum Zeitpunkt der Auswertung der Einschränkungen festgelegt bleibt .

Nach eingehenderer Betrachtung der Inhaltsansicht scheint es, dass keine automatischen Größenmasken mehr festgelegt werden.

Das Festlegen der autoresizingMask, bevor Sie Ihre Ansichten einschränken, kann dieses Problem beheben:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
        [self loadViews];
        [self constrainViews];
    }
    return self;
}
Liamnichole
quelle
Das macht den Job danke Mann. Was kann bei der Verwendung von Bearbeitungsstilen passieren?
Alexis
11
@ L14M333 Siehe den Code, den ich in diesem Kommentar hier gepostet habe - im Grunde sollten Sie nur festlegen können, self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);bevor Sie Ihre Einschränkungen hinzufügen, um das Problem zu beheben.
Smileyborg
2
Die AutoresizingMask funktioniert bei mir nicht: Die anfängliche Einschränkung ist weg, aber die 3 neuen werden hinzugefügt. "<NSAutoresizingMaskLayoutConstraint: 0x8b79740 h = - & - v = - & - UITableViewCellContentView: 0x8b44010.height == UITableViewCellScrollView: 0x8 "<NSAutoresizingMaskLayoutConstraint: 0x8b7b9f0 h = - & - v = - & - UITableViewCellScrollView: 0x8b4a130.height == LibraryCell: 0x8ac19d0.height>", "<NSAutoresizingMaskLayoutConstraint: 0x8 = 0x8ac19d0 (0)]> "
Katamphetamin
1
Funktioniert auch für mich, ich habe ungefähr eine Stunde damit zu kämpfen! Vielen Dank! <3
Mokagio
2
Nachdem ich andere Probleme mit dem automatischen Layout (iOS 7 / iOS 8) beseitigt hatte, stellte ich fest, dass die Verwendung self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);genauso gut funktioniert wie die Verwendung self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;. Ich habe dies eingefügt, updateConstraintsbevor ich die Einschränkungen zu meiner Inhaltsansicht hinzufüge.
Testen
35

Anscheinend stimmt etwas mit UITableViewCell und UICollectionViewCell unter iOS 7 mit iOS 8 SDK nicht.

Sie können die Inhaltsansicht der Zelle aktualisieren, wenn die Zelle wie folgt wiederverwendet wird:

Für statischen UITableViewController:

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }

    //your code goes here

    return cell;
}

#endif
#endif

Da Static Table View Controller fragil sind und leicht beschädigt werden können, wenn Sie Datenquellen- oder Deletegate-Methoden implementieren, gibt es Überprüfungen, die sicherstellen, dass dieser Code nur unter iOS 7 kompiliert und ausgeführt wird

Ähnlich verhält es sich mit dem dynamischen Standard-UITableViewController:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"CellID";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }
    //your code goes here       
    return cell;
}

In diesem Fall benötigen wir keine zusätzliche Überprüfung der Kompilierung, da die Implementierung dieser Methode erforderlich ist.

Die Idee ist für beide Fälle und für UICollectionViewCell gleich, wie in diesem Thread kommentiert: Die automatische Größenänderung des Frames von UICollectionViewCell contentView in der Storyboard-Prototypzelle (Xcode 6, iOS 8 SDK) tritt nur unter iOS 7 auf

KoCMoHaBTa
quelle
1
Dies ist die beste Antwort oder zumindest die beste Lösung für mich. Mein Handy (Autolayout-Regeln) hat in iOS8 mit XCode6 und in iOS7 und 6 mit XCode 5 hervorragend funktioniert. Ich aktualisiere XCode auf XCode6 und es funktioniert nicht mehr in iOS6 und 7. Also, danke !!!!
xarly
Dies ist mit Xcode 6.1 kein Problem mehr. Verwenden Sie auch nicht NSFoundationVersionNumber, siehe nshipster.com/swift-system-version-checking
onmyway133
1
@ onmyway133: Warum ist das in Xcode 6.1 kein Problem mehr? Ich habe immer noch das Problem, auch wenn ich Xcode 6.1 und iOS 8.1 SDK verwende. Das Problem tritt nur unter iOS 7 auf. Durch Ändern der Grenzen oder Festlegen einer Maske für die automatische Größenänderung wird das Problem behoben. Aber ich habe den Ansatz verwendet, der in der am häufigsten gewählten Antwort oder in den Kommentaren dazu angegeben ist.
Testen
Ja, dies ist die beste Antwort, basierend auf meinem 3-stündigen Googeln. Andere ähnliche "Lösungen" bieten nicht die vollständige Liste von cell.contentView.autoresizingMask. Nur dieses funktioniert für mein iPad 7.1-Projekt, das in Xcode 6 erstellt wurde.
Golden Thumb
13

Da Zellen wiederverwendet werden und sich die Höhe je nach Inhalt ändern kann, ist es meiner Meinung nach im Allgemeinen besser, die Priorität von Abständen auf weniger als erforderlich festzulegen.

In Ihrem Fall hat die UIImageView einen Abstand von 15 nach oben und die untere Ansicht einen Abstand von 0 nach unten. Wenn Sie die Priorität dieser Einschränkungen auf 999 (anstelle von 1000) festlegen, stürzt die App nicht ab, da die Einschränkungen nicht erforderlich sind.

Sobald die layoutSubviews-Methode aufgerufen wird, hat die Zelle die richtige Höhe und die Einschränkungen können normal erfüllt werden.

Steven
quelle
Du bist ein Genie! schöne und saubere Lösung danke :)
Blacky
9

Ich habe immer noch keine gute Lösung für Storyboards gefunden ... Einige Informationen auch hier: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188

Nach dem, was sie dort raten:

self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);

Ich habe meine Lösung induziert:

  • Klicken Sie mit der rechten Maustaste auf das Storyboard
  • Öffnen Sie als -> Quellcode
  • Suchen Sie dort nach der Zeichenfolge "44"
  • es wird so sein

.

<tableView hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="44" ...
    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatMessageCell" id="bCG-aU-ivE" customClass="ChatMessageCell">
        <rect key="frame" x="0.0" y="22" width="320" height="44"/>

.

  • Ersetzen Sie rowHeight = "44" durch rowHeight = "9999" und height = "44" durch height = "9999".
  • Klicken Sie mit der rechten Maustaste auf das Storyboard
  • Öffnen Sie As -> Interface Builder
  • Führen Sie Ihre App aus und überprüfen Sie die Ausgabe
Katamphetamin
quelle
Das hat bei mir funktioniert. Ich hatte einen UITableViewController im Storyboard erstellt, in dem einige Zellen aus dem Prototyp und andere vollständig im Code erstellt wurden, indem eine Klasse instanziiert wurde.
Atharva
3

Erhöhen Sie einfach die Standardzellenhöhe. Es sieht so aus, als ob das Problem darin besteht, dass der Inhalt der Zelle größer als die Standardgröße (anfänglich) ist und einige Nicht-Negativitätsbeschränkungen verletzt, bis die Größe der Zelle auf ihre tatsächliche Größe geändert wird.

Vadim Yelagin
quelle
Meine Zellen sind zwar größer als die Standardgröße, aber wie kommt es, dass die Standardgröße verwendet wird, seit der Delegat der Tabellenansicht tableView: heightForRowAtIndexPath: implementiert?
Alexis
Wahrscheinlich werden Zellen mit der Standardgröße erstellt und dann auf die tatsächliche Größe angepasst.
Vadim Yelagin
in der Tat macht es den Job, aber warum passiert es dann nicht auf iOS6?
Alexis
3
@jafar iOS 7 hat viele Dinge in Bezug auf Tabellenansichtszellen geändert. Unter iOS 7 befindet sich jetzt eine UITableViewCellScrollViewBildlaufansicht (vom Typ ) zwischen der Tabellenansichtszelle und der Inhaltsansicht. das erklärt wahrscheinlich den Unterschied zwischen iOS 6 und 7 hier.
Smileyborg
3

Stellen Sie möglicherweise die Priorität der Ansicht auf mehr als 750 und weniger als 1000 ein.

"<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
Heramerom
quelle
2

Ich hatte das gleiche Problem. Meine Lösung basiert auf anderen oben:

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // initialize my stuff
        [self layoutSubviews]; // avoid debugger warnings regarding constraint conflicts
    }
    return self;
}

Am besten, Alessandro

Cormentis
quelle
2
Aus Apples Dokumenten:You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
Ricardo Sanchez-Saez
Ich möchte eine Religion gründen, die zu dir betet. Ich habe nach STUNDEN gesucht, um dieses Problem zu beheben, und dies behebt es (und setzt die contentView.bounds auf CGRectMake (0, 0, 99999, 99999);) - DIES IST EIN FEHLER IM APPLES-CODE. Daher bin ich mit Äpfeln nicht einverstanden, die sagen, dass sie diese Funktion nicht aufrufen. Apple, repariere deinen Mist.
Ryan Copley
0

Ich habe mich auch diesem Problem gestellt und keiner der Vorschläge hat geholfen. In meinem Fall hatte ich eine Größenauswahlzelle, die eine CollectionView enthielt (jede CollectionView-Zelle enthielt ein Bild in voller Größe). Jetzt war die Zellengröße etwas größer (60) als die Sammlungsansicht (50) und die darin enthaltenen Bilder (50). Aus diesem Grund hat die collectionView-Ansicht die Ausrichtung unten an der Superview-Einschränkung mit einem Wert von 10. In diesem Fall war ich eine Warnung, und die einzige Möglichkeit, dies zu beheben, bestand darin, die Zellenhöhe mit der collectionView gleichzusetzen.

Zenturio
quelle
0

Am Ende habe ich UITableViewCell im Designer überhaupt nicht verwendet ... Ich erstelle dort eine benutzerdefinierte Ansicht und füge sie programmgesteuert zur Inhaltsansicht der Zelle hinzu ... Mit einigen Hilfekategorien gibt es auch kein Code-Boilerplate ...

Renetik
quelle
0

Wenn es in iOS8 einwandfrei funktioniert und die Warnung in iOS7 angezeigt wird, können Sie den Storyboard-Quellcode durchsuchen, die richtige tableviewCell finden und dann das rect-Attribut nach der tableviewCell-Zeile anhängen. Danke an Kuchumovn .

<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" .../>
                                <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
NSDeveloper
quelle