Auto-Layout: Was erzeugt Einschränkungen mit dem Namen UIView-Encapsulated-Layout-Width & Height?

74

Meine Layoutbeschränkungen sind in Interface Builder in Ordnung, aber zur Laufzeit tritt eine Ausnahme auf, da ein Teil des Frameworks feste Höhen- und Breitenbeschränkungen anwendet, die ich wirklich nicht möchte. Warum sind sie dort und wie können sie ausgeschaltet werden?

Dies sind die letzten beiden Einschränkungen, die in der protokollierten Liste angezeigt werden:

2014-04-26 09:02:58.687 BBCNews[32058:60b] 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:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>",
    "<NSLayoutConstraint:0xbf47190 UIView:0xbf4a3c0.leading == BNMyNewsCell_landscape:0xbf48b10.leading>",
    "<NSLayoutConstraint:0xbf47160 UIView:0xbf4a3c0.trailing == BNMyNewsCell_landscape:0xbf48b10.trailing>",
    "<NSLayoutConstraint:0xbf47130 BNMyNewsCell_landscape:0xbf48b10.bottom == UIView:0xbf4a3c0.bottom>",
    "<NSLayoutConstraint:0xbf47100 UIView:0xbf4a3c0.top == BNMyNewsCell_landscape:0xbf48b10.top>",
    "<NSLayoutConstraint:0xd4c3c40 'UIView-Encapsulated-Layout-Width' H:[BNMyNewsCell_landscape:0xbf48b10(304)]>",
    "<NSLayoutConstraint:0xd4c38a0 'UIView-Encapsulated-Layout-Height' V:[BNMyNewsCell_landscape:0xbf48b10(290)]>"
}
Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>
Reuben Scratton
quelle

Antworten:

95

Basierend auf einer Menge Beobachtungen glaube ich (kann aber nicht sicher wissen), dass die Einschränkungen von und Freunden benannt UIView-Encapsulated-Layout-Widthund UIView-Encapsulated-Layout-Heighterstellt werden UICollectionViewund existieren, um die von der sizeForItemAtIndexPathDelegatenmethode zurückgegebene Größe durchzusetzen . Ich denke, es ist da, um sicherzustellen, dass das UICollectionViewCellSetup von cellForItemAtIndexPathso groß wird, wie es angekündigt wurde.

Welches beantwortet meine erste Frage hier. Eine zweite Frage ist, warum die Einschränkungen unbefriedigend waren. Die Eigenhöhe der Zelle sollte dieselbe sein wie UIView-Encapsulated-Layout-Height. Auch hier weiß ich es nicht genau, aber ich vermute, dass es sich um einen Rundungsfehler handelte (dh die Eigenhöhe betrug 200,1 Pixel, die UIView-Encapsulated-Layout-Heightmöglicherweise auf 200 gerundet wurde. Die Lösung bestand darin, die Priorität der relevanten Zellenbeschränkung nur zu senken zu ermöglichen , UIView-Encapsulated-Layout-Heightdas letzte Wort zu haben.

Reuben Scratton
quelle
1
Wunderschönen. Ein ähnliches Problem in meinem Projekt wurde behoben.
Jamie Forrest
2
Dies hat meinen Einschränkungsbruch und die entsprechenden Warnmeldungen behoben, aber jetzt ist meine
Textansicht reduziert,
1
@ Alex311 hast du versucht, den Kompressionswiderstand (oder wie auch immer er heißt) in IB anzupassen? Ich habe festgestellt, dass das Erhöhen dieses Werts dazu beiträgt, das Zusammenfallen von Text zu verhindern
Orion Edwards,
1
UIView-Encapsulated-Layout-WidthUm dies hinzuzufügen, hatte ich ein ähnliches Problem mit dem 8.3-Gerät und erinnerte mich an diesen Tweet . Das Anrufen [someView layoutIfNeeded]half, das Problem zu beheben.
Rodrigo Lima
11
Ich hatte die Hoffnung auf das Beheben von Einschränkungswarnungen in der Konsole völlig aufgegeben, aber irgendwie stieß ich auf ein anderes Einschränkungsproblem, das mich zu dieser Seite führte, und ich war froh, hier zu landen und Ihre Antwort zu sehen. Ich habe gerade die Priorität auf 999 gesenkt und boom, alle meine Einschränkungen Warnungen waren weg :) Vielen Dank :) :) :)
Srikanth
45

Dies kann Ihre Frage nicht beantworten, aber es könnte anderen wie mir helfen, die von der Suche hierher gekommen sind.

Ich habe einen seltsamen Fehler bei einer fehlerhaften AutoLayout-Einschränkung erhalten, der von einer UIView-Encapsulated-Layout-WidthEinschränkung begleitet wurde , weil ich tableHeaderVieweiner Tabellenansicht eine hinzugefügt habe, deren Größe noch nicht mit AutoLayout festgelegt wurde. Das System hat also versucht, die Einschränkungen meiner Header-Unteransichten in einer Tabellenansicht mit einem Frame von anzuwenden {0,0,0,0}. Da UITableView die Kontrolle über die Breite seiner Elemente mag, wurde die generierte Breitenbeschränkung UIView-Encapsulated-Layout-Widthauf Null gesetzt, was zu allerlei Verwirrung mit meinen Header-Elementen führte, die eine Breite von 320 + pt erwarteten.

Zum Mitnehmen: Stellen Sie sicher, dass Sie Ihre Zusatz- / Kopf- / Fußzeilenansichten hinzufügen / bearbeiten, nachdem die Tabellenansicht von AutoLayout angepasst wurde.

Yerk
quelle
2
Ich möchte sicher sein, dass ich das richtig mache. Können Sie erklären, wie Sie sicher sein können, dass diese Manipulationen nach dem AutoLayout erfolgen?
Nwales
3
@nwales Das Problem tritt auf, wenn iOS versucht, Einschränkungen innerhalb einer 0 x 0-Ansicht zu gestalten. Um diesen Fehler zu vermeiden, stellen Sie einfach sicher, dass die Containeransicht (in meinem Fall die tableHeaderView) entweder angelegt oder manuell dimensioniert ist. Sie können dies tun, indem Sie die Containeransicht mit einer ungefähren Größe initialisieren, bevor Sie Einschränkungen festlegen. Wenn Sie ein reines Autolayout möchten, legen Sie die Einschränkungen der Containeransicht fest, und rufen Sie sie auf, [thatContainer layoutIfNeeded]bevor Sie die Einschränkungen für die Unteransicht festlegen. Wenn Sie den Fehler nicht erhalten und Ihre Ansichten klar sind, brauchen Sie sich keine Sorgen zu machen.
Yerk
Vielen Dank! Das Umdrehen der Reihenfolge, in der ich meine Tabellenansicht angeordnet und ihren Header zugewiesen habe, ließ diesen Fehler verschwinden =)
CodeMonkey
Die beste Lösung hierfür besteht darin, die Prioritäten Ihrer Einschränkungen zu löschen, damit sie nicht benötigt werden. Priorität auf hohe Werke setzen.
Alexander Smith
6

Ich war mit der gleichen seltsamen Einschränkung konfrontiert und hatte keine Ahnung warum, bis ich mich an das verdammte translatesAutoresizingMaskIntoConstraintsEigentum erinnerte. Stellen Sie dies ein, um falsedas Problem zu lösen. Im Hintergrund werden die Masken für die automatische Größenänderung (die alte Layout-Engine für iOS) in Einschränkungen konvertiert. Sehr oft möchten Sie diese Einschränkungen nicht und möchten Ihre eigenen. In solchen Fällen sollten Sie diese Eigenschaft auf false setzen, und es wird Ihnen gut gehen:

view.translatesAutoresizingMaskIntoConstraints = false
Zoltán
quelle
2
Auf was viewstellst du das genau ein? Ist es UITableViewCell?
Hlung
Normalerweise legen Sie es in der Ansicht fest, die Sie hinzufügen möchten.
Zoltán
4

Auf jeden Fall auf a UITableView's zu sehen tableHeaderView. Ich konnte dies mit einer benutzerdefinierten Header-Ansicht zum Laufen bringen, indem ich die Breite explizit auf die Breite tableViewnach dem Festlegen von einstellte tableHeaderViewund sie dann nach Abschluss eines Layout-Durchlaufs zurücksetzte.

Beispielcode für iOS 9, bei dem davon ausgegangen wird, dass Sie eine UITableViewMethode als tableViewund ein Element zum Konfigurieren als item: übergeben haben.

//Create the header view
self.contentDetailHeaderView = MyCustomHeaderView()

//Turn on autolayout
self.contentDetailHeaderView.translatesAutoresizingMaskIntoConstraints = false

//Add the header to the table view
tableView.tableHeaderView = self.contentDetailHeaderView

//Pin the width  
let widthConstraint = NSLayoutConstraint(item: self.contentDetailHeaderView,
    attribute: .Width,
    relatedBy: .Equal,
    toItem: tableView,
    attribute: .Width,
    multiplier: 1,
    constant: 0)

tableView.addConstraint(widthConstraint)

//Do whatever configuration you need to - this is just a convenience method I wrote on my header view.
self.contentDetailHeaderView.setupForItem(item)

//Lay out the configured view
self.contentDetailHeaderView.layoutIfNeeded()

//Reset the table header view, because ¯\_(ツ)_/¯
tableView.tableHeaderView = self.contentDetailHeaderView

Ein paar Notizen, vor allem, wenn ich das noch einmal nachschaue, weil ich die Erinnerung an einen Goldfisch habe:

  • Sie müssen dies nicht von aufrufen viewDidLayoutSubviews- ich konnte diese Technik verwenden, solange die tableViewwährend des Setups die entsprechende Breite hat.
  • Sie müssen sicherstellen, dass Ihre Header-Ansicht so eingerichtet ist, dass sich die Größe automatisch ändert. Dazu habe ich ein erstellt .xib und dann sichergestellt, dass alle Elemente angeheftet sind, sodass die Höhe aktualisiert wird, wenn sich die Breite der Ansicht ändert.
  • Wenn Sie versuchen, dies zu tun viewForHeaderInSection, ist es wahrscheinlich besser, sich etwas außerhalb des Bildschirms zu schnappen, das Sie nach dieser Technik auslegen können . Ich hatte nicht viel Glück mit den selbstleimenden Teilen.
DesignatedNerd
quelle
Beeindruckend. Nach einer halben Stunde Herumspielen ... funktioniert das tatsächlich! Ich verstehe nicht, warum es benötigt wird, aber es funktioniert. Ziemlich beschissen, dass wir es so machen müssen.
Joris Mans
4

In iOS 11 treten unzählige Layoutkonflikte auf, die Verweise auf diese Einschränkungen enthalten und tatsächlich über das translatesAutoresizingMaskIntoConstraintsFlag hinzugefügt werden . Es scheint, dass in iOS 11 viel mehr AutoLayout-Magie auftritt, wenn eine Ansicht zur Hierarchie hinzugefügt wird, als nur, wenn die Ansicht angelegt ist (wie es in früheren iOS-Versionen zu funktionieren schien).

Dies ist der Fall, auf den wir gestoßen sind:

  • Erstellen Sie eine Ansicht, deren internes Layout beim Definieren der Ansichtsgröße hilft (z. B. hat die Ansicht interne Einschränkungen, einschließlich expliziter Auffüllung usw.).
  • *** Fügen Sie diese Ansicht der Hierarchie hinzu.
  • Setzen Sie die translatesAutoresizingMaskIntoConstraints einige Zeit später auf false, bevor das Layout übergeben wird.

Der zweite Schritt (***) führt zu einem Konflikt, da das System der Ansicht zum Zeitpunkt des Hinzufügens der Ansicht zur Hierarchie Größenbeschränkungen von Null hinzufügt. Wir haben die Einstellung translatesAutoresizingMaskIntoConstraintsspäter vorgenommen, weil wir das PureLayout-Framework verwendet haben, das dieses Flag automatisch korrekt setzt, wenn Sie die Ansicht einschränken. In iOS 11 müssen Sie jedoch daran denken, die translatesAutoresizingMaskIntoConstraintsErstellung zum Zeitpunkt der Erstellung zu deaktivieren , bevor die Ansicht zum hinzugefügt wird Hierarchie.

Ich vermute, Apple dachte, dass es weitaus hilfreicher als schmerzhaft wäre, dieses Flag auf JA zu setzen. Dies war leider nicht der Fall.

Tyler
quelle
PureLayout bietet initForAutoLayoutund newAutoLayoutViewInitialisierer, die translatesAutoresizingMaskIntoConstraintsauf false gesetzt sind und in ALView-Unterklassen aufgerufen werden können
killianke
3

Ich habe diesen Fehler unter allen möglichen Umständen erhalten (nicht unbedingt an UICollectionView und Freunde gebunden, wie in der richtigen Antwort hier vorgeschlagen).

Meine Art, damit umzugehen, bestand darin, einfach alle Einschränkungen zu löschen und sie dann wieder aufzubauen (nur dieses Mal habe ich keine Angst davor, dass meine Einschränkungen mit diesen vorab erstellten kollidieren):

also im Code:

UIView *parentView = [viewInQuestion superview];
[parentView clearConstraintsOfSubview:viewInQuestion];

Wo clearConstraintsOfSubviewist eine Kategoriemethode in UIView:

- (void)clearConstraintsOfSubview:(UIView *)subview
{
    for (NSLayoutConstraint *constraint in [self constraints]) {
        if ([[constraint firstItem] isEqual:subview] || [[constraint secondItem] isEqual:subview]) {
            [self removeConstraint:constraint];
        }
    }
}
Abbood
quelle
2

Ich hatte ein ähnliches Problem und löste es wie folgt.

  • Umgebung: Swift 5.0, xcode 10.2.1, Ansichten programmgesteuert einstellen

  • Warnmeldung: Einschränkungen können nicht gleichzeitig erfüllt werden ... 'UIView-Encapsulated-Layout-Width' UIView: 0x0000000000.width == 0 (aktiv)> ")

  • Code mit Warnung

    override func loadView() {
    
    view = UIView()
    
    /// Adds the subviews to the view and sets their properties and constraints
    setupViews()
    
    }
    
  • Code, der die Warnung löschte

    override func loadView() {
    
    /// Needed to set the frame of the root view to the window frame.
    let window = UIWindow()
    view = UIView(frame: window.frame)
    
    /// Adds the subviews to the view and sets their properties and constraints
    setupViews()
    }
    
  • Hinweise zur loadView () -Methode: "Wenn Sie Ihre Ansichten mit Interface Builder erstellen und den Ansichtscontroller initialisieren, dürfen Sie diese Methode nicht überschreiben. Sie können diese Methode überschreiben, um Ihre Ansichten manuell zu erstellen. Wenn Sie dies möchten Weisen Sie der Ansichtseigenschaft die Stammansicht Ihrer Ansichtshierarchie zu. Die von Ihnen erstellten Ansichten sollten eindeutige Instanzen sein und nicht mit anderen View-Controller-Objekten geteilt werden. Ihre benutzerdefinierte Implementierung dieser Methode sollte nicht super aufrufen. " - Apple-Dokumentation

  • Hinweise zur Stammansicht:

    "Wenn Sie Ansichten lieber programmgesteuert erstellen möchten, überschreiben Sie dazu die loadView-Methode Ihres View-Controllers. Ihre Implementierung dieser Methode sollte Folgendes bewirken:

    Erstellen Sie ein Stammansichtsobjekt. Die Stammansicht enthält alle anderen Ansichten, die Ihrem Ansichtscontroller zugeordnet sind. Normalerweise definieren Sie den Rahmen für diese Ansicht so, dass er der Größe des App-Fensters entspricht, das selbst den Bildschirm ausfüllen soll. Der Rahmen wird jedoch basierend auf der Anzeige Ihres View Controllers angepasst. Siehe "Ändern der Größe der Controller-Ansicht".

    Sie können ein generisches UIView-Objekt, eine von Ihnen definierte benutzerdefinierte Ansicht oder eine andere Ansicht verwenden, die skaliert werden kann, um den Bildschirm auszufüllen.

    Erstellen Sie zusätzliche Unteransichten und fügen Sie sie der Stammansicht hinzu. "- Alte Apfeldokumentation?

Ben Oveson
quelle
2

Nachdem ich eine Weile mit dem Kopf geschlagen hatte, fand ich diesen Link . In meinem Fall passierte es in der UITableViewHeaderFooterView, als ich insertRows oder deleteRows von meinem UIVieController verwendete. 'EstimatedSectionHeaderHeight' und 'EstimatedRowHeight' wurden gesetzt, meine Einschränkungen wurden dreimal wiederholt ... Der angezeigte Fehler war:

"<NSLayoutConstraint:0x280648140 H:|-(8)-[UIImageView:0x106e94860]   (active, names: '|':DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50 )>",
"<NSLayoutConstraint:0x280648230 H:[UIImageView:0x106e94860]-(8)-[DAT_Air_Vinyl.MainLabel:0x106ea5750'The Coral - The Invisible...']   (active)>",
"<NSLayoutConstraint:0x280648410 H:[UIButton:0x106ea5a40]-(8)-|   (active, names: '|':DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50 )>",
"<NSLayoutConstraint:0x280648460 H:[DAT_Air_Vinyl.MainLabel:0x106ea5750'The Coral - The Invisible...']-(8)-[UIButton:0x106ea5a40]   (active)>",
"<NSLayoutConstraint:0x2806493b0 'UIView-Encapsulated-Layout-Width' DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50.width == 0   (active)>"

Wie auf dem Link angegeben:

" Wenn Sie mit bestimmten Animationstypen Zeilen einfügen oder löschen löschen, animiert UIKit die Zeilenhöhe von 0 auf volle Höhe oder zurück. Am 0-Ende dieser Animation können die Layoutgleichungen nicht gelöst werden, wenn die gesamte vertikale Achse auf Priorität gesetzt ist = 1000. Aber senken Sie nur eine Einschränkung auf 999 - sagen Sie, dass der untere Bereich zum Überwachen des Randes - und alles ist in Ordnung; der Inhalt wird nur außerhalb der Grenzen der Zelle abgelegt. "

Die Lösung bestand darin, die führende Priorität von UIImageView auf 999 (oder niedriger auf 1000) zu setzen.

Reimond Hill
quelle
1

In meinem Fall habe ich versehentlich zweimal meine programmatischen Einschränkungen festgelegt. Sobald ich den doppelten Anruf entfernt hatte, verschwanden die Konflikte.

tcarey
quelle
1

Ich habe dieses Problem festgestellt, wenn ich AL create tableviewHeader verwende

Ich initiiere die Tabellenansicht wie unten

let table = UITableView.init(frame: .zero, style: .grouped)
// set table constraint ...

dann erstelle ich tableviewHeader mit AutoLayout.

"<NSLayoutConstraint:0x600003d7d130 'UIView-Encapsulated-Layout-Width' UIView:0x7fe92bc55260.width == 0 (active)>"

Es wird ein symbolischer Haltepunkt angezeigt

Nachdem ich @Yerk verwiesen habe, ist die Antwort. Ich ändere den Frame, wenn ich tableView initiiere

let rect = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 0)
let table = UITableView.init(frame:rect , style: .grouped)

Das Problem scheint gelöst zu sein

kkklc
quelle
0

Beim Testen von Split View auf dem iPad Pro wurde ein ähnliches Problem festgestellt, und die Antwort von DesignatedNerd funktionierte, aber ich brauchte nicht so viel Code. Folgendes habe ich verwendet:

[self.tableView.tableHeaderView setTranslatesAutoresizingMaskIntoConstraints:NO];

NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
                                                                   attribute:NSLayoutAttributeWidth
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:self.tableView
                                                                   attribute:NSLayoutAttributeWidth
                                                                  multiplier:1
                                                                    constant:0];
NSLayoutConstraint *yConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
                                                               attribute:NSLayoutAttributeTop
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.tableView
                                                               attribute:NSLayoutAttributeTop
                                                              multiplier:1
                                                                constant:0];


[self.tableView addConstraints:@[widthConstraint, yConstraint]];

Beachten Sie das Hinzufügen der Y-Einschränkung, die den oberen Rand der tableHeaderView an den oberen Rand der tableView bindet.

Declan Deckerson
quelle
0

Ich hatte das gleiche Problem beim Hinzufügen von Einschränkungen zu einem Tabellenansichts-Header. Es scheint aufzutreten, wenn Einschränkungen mit festgelegten Konstanten hinzugefügt werden, wenn die Grenzen des Headers (0,0,0,0) waren. Ich habe es geschafft, dies zu beheben, indem ich die Einschränkungen in der Layout-Unteransichtsmethode nur hinzugefügt habe, wenn die Grenzen des Headers nicht (0,0,0,0) waren.

    if self.bounds == CGRect.zero {
        return
    }
DairySeeker
quelle
0

Die Einschränkung UIView-Encapsulated-Layout-Heightwird mit dem von Ihnen festgelegten Wert erstellttableView.estimatedSectionHeaderHeight

Maq
quelle