webentwicklung-frage-antwort-db.com.de

UIStackView "Einschränkungen können nicht gleichzeitig erfüllt werden" für ausgeblendete "Squished" -Ansichten

Wenn meine UIStackView "Zeilen" zerquetscht werden, werfen sie AutoLayout Warnungen. Sie werden jedoch gut angezeigt und nichts anderes ist falsch als diese Art von Protokollierungen:

Einschränkungen können nicht gleichzeitig erfüllt werden. Möglicherweise ist mindestens eine der Einschränkungen in der folgenden Liste eine, die Sie nicht möchten. Versuchen Sie Folgendes: (1) Sehen Sie sich jede Einschränkung an und versuchen Sie herauszufinden, was Sie nicht erwarten. (2) Finden Sie den Code, der die unerwünschte (n) Einschränkung (en) hinzugefügt hat, und korrigieren Sie ihn. (Hinweis: Wenn Sie NSAutoresizingMaskLayoutConstraints sehen, das Sie nicht verstehen, lesen Sie die Dokumentation für die UIView -Eigenschaft translatesAutoresizingMaskIntoConstraints.) (

Ich bin mir noch nicht sicher, wie ich das beheben soll, aber es scheint nichts zu brechen, außer nur nervig zu sein.

Weiß jemand, wie man es löst? Interessanterweise sind die Layoutbeschränkungen häufig mit 'UISV-Hiding' markiert, was darauf hinweist, dass die minimalen Höhenangaben für Unteransichten oder ähnliches in diesem Fall möglicherweise ignoriert werden sollten.

89
Ben Guild

Dieses Problem tritt auf, weil eine Unteransicht, die sich innerhalb von UIStackView befindet und ausgeblendet ist, zunächst auf Null gesetzt wird, um sie zu animieren.

Ich habe den folgenden Fehler erhalten:

2015-10-01 11:45:13.732 <redacted>[64455:6368084] 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:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5be508d0 V:|-(8)-[UISegmentedControl:0x7f7f5bec4180]   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5bdfbda0 'UISV-hiding' V:[UIView:0x7f7f5be69d30(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Ich habe versucht, ein UIView in mein UIStackView einzufügen, das an jeder Kante einen UISegmentedControl -Einsatz von 8 Punkten enthält.

Wenn ich es auf versteckt setze, würde es versuchen, die Containeransicht auf eine Höhe von Null zu beschränken, aber da ich eine Reihe von Einschränkungen von oben nach unten habe, gab es einen Konflikt.

Um das Problem zu beheben, habe ich meine Priorität für 8pt top und bottom von 1000 auf 999 geändert, sodass UISV-hiding Constraint kann dann bei Bedarf Priorität haben.

196
liamnichols

Ich hatte ein ähnliches Problem, das nicht einfach zu lösen war. In meinem Fall hatte ich eine Stapelansicht, die in eine Stapelansicht eingebettet war. In der internen UIStackView wurden zwei Beschriftungen und ein Abstand ungleich Null angegeben.

Wenn Sie addArrangedSubview () aufrufen, werden automatisch Einschränkungen erstellt, die den folgenden ähneln:

V:|[innerStackView]|              | = outerStackView

  V:|[label1]-(2)-[label2]|       | = innerStackView

Wenn Sie nun versuchen, die InnerStackView auszublenden, wird eine mehrdeutige Einschränkungswarnung angezeigt.

Um zu verstehen warum, wollen wir zuerst sehen, warum dies nicht passiert, wenn innerStackView.spacing Gleich 0 Ist. Wenn Sie innerStackView.hidden = true Aufrufen, war @liamnichols korrekt ... das outerStackView fängt diesen Aufruf auf magische Weise ab und erstellt eine 0 - Höhe UISV -hiding Einschränkung mit Priorität 1000 (erforderlich). Vermutlich dient dies dazu, dass Elemente in der Stapelansicht außerhalb der Ansicht animiert werden, falls der Code zum Ausblenden in einem UIView.animationWithDuration() -Block aufgerufen wird. Leider scheint es keine Möglichkeit zu geben, das Hinzufügen dieser Einschränkung zu verhindern. Trotzdem wird die Warnung "Nicht gleichzeitig erfüllbare Bedingungen" (USSC) nicht angezeigt, da Folgendes auftritt:

  1. die Höhe von label1 ist auf 0 gesetzt
  2. der Abstand zwischen den beiden Beschriftungen wurde bereits als 0 definiert
  3. die Höhe von label2 ist auf 0 gesetzt
  4. die Höhe von innerStackView wird auf 0 gesetzt

Es ist klar zu sehen, dass diese 4 Bedingungen erfüllt werden können. Die Stapelansicht glättet einfach alles in ein Pixel mit der Höhe 0.

Zurück zum Buggy-Beispiel: Wenn wir spacing auf 2 Setzen, haben wir jetzt folgende Einschränkungen:

  1. die Höhe von label1 ist auf 0 gesetzt
  2. Der Abstand zwischen den beiden Beschriftungen wurde von der Stapelansicht automatisch als 2 Pixel hoch mit 1000er Priorität erstellt.
  3. die Höhe von label2 ist auf 0 gesetzt
  4. die Höhe von innerStackView wird auf 0 gesetzt

Die Stapelansicht darf nicht beide 0 Pixel und der Inhalt 2 Pixel hoch sein. Die Auflagen können nicht erfüllt werden.

Hinweis: Sie können dieses Verhalten anhand eines einfacheren Beispiels sehen. Fügen Sie einer Stapelansicht einfach eine UIView als angeordnete Unteransicht hinzu. Legen Sie dann eine Höhenbeschränkung für dieses UIView mit 1000-Priorität fest. Rufen Sie jetzt hide auf.

Hinweis: Aus irgendeinem Grund geschah dies nur, wenn meine Stapelansicht eine Unteransicht einer UICollectionViewCell oder einer UITableViewCell war. Sie können dieses Verhalten jedoch auch außerhalb einer Zelle reproduzieren, indem Sie innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) in der nächsten Ausführungsschleife aufrufen, nachdem Sie die innere Stapelansicht ausgeblendet haben.

Hinweis: Auch wenn Sie versuchen, den Code in einer UIView.performWithoutAnimations auszuführen, fügt die Stapelansicht eine Höhenbeschränkung von 0 hinzu, die die USSC-Warnung verursacht.


Es gibt mindestens 3 Lösungen für dieses Problem:

  1. Bevor Sie ein Element in einer Stapelansicht ausblenden, prüfen Sie, ob es sich um eine Stapelansicht handelt, und ändern Sie in diesem Fall das spacing auf 0. Dies ist ärgerlich, weil Sie den Vorgang umkehren müssen (und sich den ursprünglichen Abstand merken müssen), wenn Sie den Inhalt erneut anzeigen.
  2. Rufen Sie removeFromSuperview auf, anstatt Elemente in einer Stapelansicht auszublenden. Dies ist umso ärgerlicher, als Sie dies tun müssen, wenn Sie den Vorgang umkehren Denken Sie daran, wo das entfernte Element einzufügen. Sie könnten optimieren, indem Sie nur removeArrangedSubview aufrufen und dann ausblenden, aber es ist noch viel Buchhaltung erforderlich.
  3. Verschachtelte Stapelansichten (die nicht null sind spacing) in eine UIView einbinden. Geben Sie mindestens eine Einschränkung als nicht erforderliche Priorität an (999 oder niedriger). Dies ist die beste Lösung, da Sie keine Buchhaltung durchführen müssen. In meinem Beispiel habe ich zwischen der Stapelansicht und der Wrapperansicht bei 1000 obere, vordere und hintere Einschränkungen erstellt und dann eine 999-Einschränkung vom unteren Rand der Stapelansicht zur Wrapperansicht erstellt. Auf diese Weise wird die 999-Einschränkung aufgehoben und die USSC-Warnung wird nicht angezeigt, wenn in der äußeren Stapelansicht eine Einschränkung für die Höhe Null erstellt wird. (Hinweis: Dies ähnelt der Lösung für Sollten die contentView.translatesAutoResizingMaskToConstraints einer UICollectionViewCell-Unterklasse auf false gesetzt sein )

Zusammenfassend sind die Gründe für dieses Verhalten:

  1. Apple erstellt automatisch 1000 Prioritätsbeschränkungen für Sie, wenn Sie einer Stapelansicht verwaltete Unteransichten hinzufügen.
  2. Apple erstellt automatisch eine Einschränkung für die Höhe 0, wenn Sie eine Unteransicht einer Stapelansicht ausblenden.

Hatte Apple entweder (1) erlaubt Ihnen, die Priorität von Beschränkungen (insbesondere von Abstandshaltern) anzugeben, oder (2) erlaubt Ihnen, die automatische zu deaktivieren ) UISV-Hiding Einschränkung, würde dieses Problem leicht gelöst werden.

48
Senseful

In den meisten Fällen kann dieser Fehler behoben werden, indem die Einschränkungspriorität verringert wird, um Konflikte zu beseitigen.

6
Luciano Almeida

Wenn Sie eine Ansicht auf ausgeblendet setzen, versucht das UIStackview, sie zu animieren. Wenn Sie diesen Effekt erzielen möchten, müssen Sie die richtige Priorität für die Einschränkungen festlegen, damit sie nicht in Konflikt geraten (wie von vielen oben vorgeschlagen).

Wenn Sie sich jedoch nicht für die Animation interessieren (vielleicht verstecken Sie sie in ViewDidLoad), können Sie removeFromSuperview einfach erstellen, was den gleichen Effekt hat, jedoch ohne Probleme mit Einschränkungen, da diese zusammen mit entfernt werden die Aussicht.

2
Oren

Bei eingebetteten Stapelansichten sind die gleichen Fehler aufgetreten, obwohl zur Laufzeit alles einwandfrei funktionierte.

Ich habe die Einschränkungsfehler behoben, indem ich zuerst alle Unter-Stapel-Ansichten ausgeblendet habe (Einstellung isHidden = true), bevor Sie die übergeordnete Stapelansicht ausblenden.

Dies hatte nicht die Komplexität, untergeordnete Ansichten zu entfernen und einen Index für das Hinzufügen zu pflegen.

Hoffe das hilft.

1
Will Stevens

Stellen Sie zunächst, wie andere vorgeschlagen haben, sicher, dass die Einschränkungen, die Sie steuern können, d. H. Nicht die mit UIStackView verbundenen Einschränkungen, auf Priorität 999 gesetzt sind, damit sie überschrieben werden können, wenn die Ansicht ausgeblendet ist.

Wenn das Problem weiterhin auftritt, liegt das Problem wahrscheinlich an den Abständen in den ausgeblendeten StackViews. Meine Lösung bestand darin, ein UIView als Abstandshalter hinzuzufügen und den UIStackView-Abstand auf Null zu setzen. Setzen Sie dann die Einschränkungen View.height oder View.width (abhängig von einem vertikalen oder horizontalen Stapel) auf den Abstand des StackView .

Passen Sie dann die Prioritäten für die Inhaltsanpassung und den Inhaltskomprimierungswiderstand Ihrer neu hinzugefügten Ansichten an. Möglicherweise müssen Sie auch die Verteilung des übergeordneten StackView ändern.

All dies kann in Interface Builder durchgeführt werden. Möglicherweise müssen Sie zusätzlich einige der neu hinzugefügten Ansichten programmgesteuert ausblenden/einblenden, um unerwünschte Abstände zu vermeiden.

1
Peter Coyle

Ich habe kürzlich mit automatischen Layoutfehlern gerungen, als ich ein UIStackView versteckte. Anstatt ein paar Bücher zu führen und Stapel in UIViews zu wickeln, habe ich mich dafür entschieden, eine Steckdose für meine parentStackView und Steckdosen für die Kinder zu erstellen, die ich verstecken/ausblenden möchte.

@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!

Im Storyboard sieht mein ParentStack folgendermaßen aus:

enter image description here

Es hat 4 Kinder und jedes der Kinder hat eine Reihe von Stapelansichten in sich. Wenn Sie eine Stapelansicht ausblenden und wenn es auch UI-Elemente gibt, die Stapelansichten sind, wird ein Strom von Fehlern beim automatischen Layout angezeigt. Anstatt mich zu verstecken, entschied ich mich, sie zu entfernen.

In meinem Beispiel enthält parentStackViews ein Array der 4 Elemente: Top Stack View, StackViewNumber1, Stack View Number 2 und Stop Button. Ihre Indizes in arrangedSubviews sind 0, 1, 2 bzw. 3. Wenn ich eine ausblenden möchte, entferne ich sie einfach aus dem Array parentStackView'sarrangedSubviews. Da es nicht schwach ist, bleibt es im Speicher und Sie können es später einfach wieder an den gewünschten Index setzen. Ich initialisiere es nicht neu, daher bleibt es nur hängen, bis es benötigt wird, aber es bläht den Speicher nicht auf.

Grundsätzlich können Sie ...

1) Ziehen Sie IBOutlets für Ihren übergeordneten Stapel und die Kinder, die Sie ausblenden möchten, in das Storyboard.

2) Wenn Sie sie ausblenden möchten, entfernen Sie den Stapel, den Sie ausblenden möchten, aus dem Array parentStackView'sarrangedSubviews.

3) Rufen Sie self.view.layoutIfNeeded() mit UIView.animateWithDuration Auf.

Beachten Sie, dass die letzten beiden stackViews nicht weak sind. Sie müssen sie aufbewahren, wenn Sie sie einblenden.

Angenommen, ich möchte stackViewNumber2 ausblenden:

parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()

Dann animiere es:

UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

Wenn Sie einen stackViewNumber2 Später "einblenden" möchten, können Sie ihn einfach in den gewünschten parentStackViewarrangedSubViews - Index einfügen und das Update animieren.

parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)

// Then animate it
UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

Ich fand, dass das viel einfacher ist, als über Einschränkungen Buch zu führen, mit Prioritäten herumzuspielen usw.

Wenn Sie etwas haben, das Sie standardmäßig ausblenden möchten, können Sie es einfach auf dem Storyboard anordnen und in viewDidLoad entfernen und ohne die Animation mit view.layoutIfNeeded() aktualisieren.

1
Adrian

Basierend auf der Antwort von @ Senseful finden Sie hier eine UIStackView-Erweiterung, mit der Sie eine Stapelansicht in eine Ansicht einschließen und die von ihm empfohlenen Einschränkungen anwenden können:

/// wraps in a `UIView` to prevent autolayout warnings when a stack view with spacing is placed inside another stack view whose height might be zero (usually due to `hidden` being `true`).
/// See http://stackoverflow.com/questions/32428210
func wrapped() -> UIView {
    let wrapper = UIView()
    translatesAutoresizingMaskIntoConstraints = false
    wrapper.addSubview(self)

    for attribute in [NSLayoutAttribute.Top, .Left, .Right, .Bottom] {
        let constraint = NSLayoutConstraint(item: self,
                                            attribute: attribute,
                                            relatedBy: .Equal,
                                            toItem: wrapper,
                                            attribute: attribute,
                                            multiplier: 1,
                                            constant: 0)
        if attribute == .Bottom { constraint.priority = 999 }
        wrapper.addConstraint(constraint)
    }
    return wrapper
}

Anstatt Ihr stackView hinzuzufügen, verwenden Sie stackView.wrapped().

1
Ben Packard

Ich wollte das ganze StackView auf einmal verstecken, aber ich bekam die gleichen Fehler wie das OP, dies hat es für mich behoben:

for(UIView *currentView in self.arrangedSubviews){
    for(NSLayoutConstraint *currentConstraint in currentView.constraints){
        [currentConstraint setPriority:999];
    }
}
0
sp00ky

Senseful hat eine hervorragende Antwort auf die oben genannte Problemursache geliefert, sodass ich gleich zur Lösung komme.

Sie müssen lediglich die Priorität aller stackView-Einschränkungen auf weniger als 1000 setzen (999 erledigen die Arbeit). Wenn für die Stapelansicht beispielsweise die Einschränkung "Links", "Rechts", "Oben" und "Unten" gilt, sollten alle 4 Einschränkungen eine niedrigere Priorität als 1000 haben.

0
Linh Ta

Möglicherweise haben Sie während der Arbeit mit einer bestimmten Größenklasse (z. B. wCompact hRegular) eine Einschränkung erstellt und dann beim Wechsel zu einer anderen Größenklasse (z. B. wAny hAny) ein Duplikat erstellt. Überprüfen Sie die Einschränkungen der UI-Objekte in verschiedenen Größenklassen und stellen Sie fest, ob Anomalien mit den Einschränkungen vorliegen. Sie sollten die roten Linien sehen, die auf kollidierende Einschränkungen hinweisen. Ich kann kein Bild einfügen, bis ich 10 Rufpunkte erhalte. Entschuldigung: /

0
F.M

Ich hatte eine Reihe von Knöpfen mit Höhenbeschränkungen. Dies geschieht, wenn eine Schaltfläche ausgeblendet ist. Das Festlegen der Priorität dieser Schaltflächenhöhenbeschränkung auf 999 hat das Problem behoben.

0
Niklas