15. Januar 2019

Auto Layout: Positionierung mit Layout-Constraints

Mit Layout-Constraints können Views regelbasiert positioniert werden. Damit kann eine Ausrichtung für die unterschiedlichen Bildschirmauflösungen und für Hoch- und Querformat realisiert werden. Sie vereinfachen auch die Wiederverwendung von Views, denn flexibel positioniert könnten zum Beispiel für das iPhone entwickelte Screens als Teil eines iPad-Screens verwendet werden. Zudem sind sie sehr hilfreich für mehrsprachige Apps, bei denen die Texte je nach Sprache unterschiedlich viel Platz benötigen.

Constraints sind vergleichbar mit einer wörtlichen Erklärung, mit der man das Layout beschreiben würde:

Layout Contraint

  • nach rechts 20pt Abstand zur folgenden Bildschirmkante
  • nach links 20pt Abstand zur vorausgehenden Bildschirmkante
  • nach oben 35pt Abstand zur Bildschirmkante/Status-Bar

Solche Layout-Regeln können im Interface Builder definiert werden:

Layout Constraint

Dabei wird immer eine Beziehung zwischen zwei Elementen hergestellt: Im Beispiel wird die linke/vorausgehende Kante des Buttons (Button.Leading) mit der linken Kante des Superviews (Superview.Leading) gleichgesetzt (Equal) und dabei mit Constant ein Abstand von 20 Punkt festgelegt. Alle definierten Regeln bilden ein Gleichungssystem, welches gelöst wird um eine Positionierung zu finden, die alle Constraints erfüllt. Das bedeutet auch, dass Constraints gleichberechtigt sind und, sofern keine Prioritäten definiert werden, keine bestimmte Reihenfolge haben, in der sie zur Anwendung kommen.

Im obigen Beispiel ist mit den drei Regeln die Positionierung des Buttons vollständig beschrieben: Die Höhe des Buttons ergibt sich automatisch aus der Größe des Button-Textes (intrinsic content size), da dafür kein expliziter Constraint angelegt wurde.

Constraints im Interface Builder erstellen

Da sich die Layout-Regeln nicht in jedem Fall aus der Positionierung von Steuerelementen ableiten lassen, müssen Layouts-Constraints explizit erstellt und konfiguriert werden. Dazu gibt es drei verschiedene Möglichkeiten:

Constraints erstellen: Automatisches Beheben von Layout-Problemen

Mit Add Missing Constraints bzw. Reset to Suggested Constraints im Resolve Auto Layout Issues-Menü werden automatisch Constraints anhand der aktuellen Positionierung abgeleitet:

Resolve Auto Layout Issues: Reset to Suggested Constraints

So erstellte Constraints sind ein möglicher Ausgangspunkt, müssen aber meist manuell nachgebessert werden, da sich Layout-Regeln häufig nicht korrekt aus einer bestimmten Positionierung ableiten lassen.

Constraints erstellen: Constraints mit der rechten Maustaste ziehen

Alternativ können Layout-Constraints erstellt werden, indem man mit gedrückter rechter Maustaste (bzw. gedrückter Ctrl-Taste) von dem auszurichtendem View zieht auf das View, an dem sich die Ausrichtung orientieren soll. Dabei spielt die Richtung eine Rolle - zieht man nach links oder rechts werden Constraints zur horizontalen Ausrichtung angeboten, zieht man nach oben oder unten werden Constraints zur vertikalen Ausrichtung angeboten, zieht man schräg, werden beide Richtungen angeboten:

Constraints mit der rechten Maustaste ziehen

Existiert bereits eine entsprechende Regel, wird in dem Menü ein Punkt vor dem Constraint angezeigt. Mit gedrückter Shift-Taste können mehrere Constraints auf einmal angelegt werden:

Mehrere Constraints auf einmal anlegen

Constraints erstellen: Add New Constraints

Mit Add New Constraints können mehrere Layout-Regeln komfortabel auf einmal angelegt werden:

Constraints per Add New Constraints anlegen

Constraints konfigurieren und bearbeiten

Erstellte Constraints können im Interface Builder ausgewählt und konfiguriert werden. Dazu empfiehlt sich insbesondere die Constraints-Liste im Size Inspector. Hier werden alle Layout-Regeln, die für das ausgewählte View gelten, angezeigt:

Constraints im Interface Builder auswählen und bearbeiten

Wird die Positionierung verändert, nachdem Constraints erstellt wurden, werden die Constraints nicht automatisch angepasst. Interface Builder zeigt lediglich die Abweichung zwischen den aktuellen Frames und der Positionierung, die sich laut der Constraints ergeben würde, orange an:

Abweichung Positionierung laut Frames und laut Constraints

Mit Update Constraint Constants können die Constraints entsprechend angepasst werden. Alternativ kann mit Update Frames die Positionierung wieder in den Zustand laut Constraints zurückgesetzt werden:

Update Frames / Update Constraints

Das heißt: Die Frames/die Positionierung der Steuerelemente zur Design-Zeit und die Positionierung, die sich laut der Layout-Constraints ergeben würden, sind zwei verschiedene Dinge. Interface Builder zeigt Abweichungen zwischen den Frames und der Positionierung laut Layout-Constraints an und erlaubt die Constraints gemäß der Frames anzupassen und umgekehrt.

Layout-Probleme

Bei der Verwendung von Constraints können Probleme auftreten. Dies wird dadurch kenntlich gemacht, dass ein Layout-Problem in der Outline angezeigt wird. Hier fehlt beispielsweise ein Constraint der die Positionierung nach oben festlegt - die Layout-Constraints sind unterbestimmt:

Layout Problem: Unterbestimmte Layout-Constraints

Ein weiteres mögliches Problem sind überbestimmte Constraints - Regeln die sich mit anderen Regeln widersprechen. Diese werden rot angezeigt und weitere Informationen können über den Marker in der Outline abgerufen werden. Hier sind beispielsweise zwei sich widersprechende Layout-Regeln für den Abstand nach oben angelegt:

Layout-Probleme: Konflikte / Überbestimmte Constraints

Vorschau für verschiedene Gerätegrößen

Mit View as: ... kann im Storyboard die Gerätegröße und Drehung ausgewählt werden, mit der die Controller im Storyboard-Editor angezeigt werden:

Xcode Storyboard Preview

Alternativ kann über den Assistant Editor die Storyboard-Vorschau aktiviert werden. Diese erlaubt die parallele Anzeige für mehrere Bildschirmgrößen und Drehungen:

Storyboard-Vorschau im Assistant Editor

Tutorial 1: Einführung in die Verwendung von Layout-Constraints

  1. Erstellen Sie für dieses Tutorial ein leeres Single View App-Xcode-Projekt oder fügen Sie einem bestehenden Storyboard einen neuen, leeren View-Controller hinzu. Platzieren Sie in der rechten, oberen Ecke des Views einen Button. Vergrößen Sie diesen etwas und setzen Sie im Attributes Inspector eine Hintergrundfarbe:

    Neuer View-Controller mit Button
  2. Blenden Sie den Assistant Editor ein, aktivieren Sie via Preview die Vorschau und fügen Sie mit dem +-Button links unten einige zusätzliche Gerätegrößen hinzu:

    Neuer View-Controller mit Button
  3. Aktuell erfolgt die Positionierung über die Frame-Eigenschaft mittels absoluter Angaben in Punkt, die Sie im Size Inspector finden:

    Size Inspector
  4. Ziehen Sie diagonal mit gedrückter rechter Maustaste (bzw. gedrückter ^-Taste) vom Button auf das umgebende View. Wählen Sie mit gedrückter Shift-Taste (Mehrfachauswahl) Trailing Space to Safe Area und Top Space to Safe Area aus und bestätigen Sie die Auswahl mit Enter:

    Layout-Constraint erstellen

    Tipp: Durch das Ziehen in diagonaler Richtung werden sowohl Constraints in der horizontalen als auch vertikalen Richtung angeboten. Sie können auch horizontal oder vertikal ziehen um nur Constraints in der einen Richtung angeboten zu bekommen:

    Diagonal/Horizontal/Vertikal ziehen

  5. Prüfen Sie über die Outline, dass zwei Layout-Constraints erstellt wurden:

    Erstellte Constraints

    Wählen Sie einen der Constraints aus und machen Sie sich mit den Eigenschaften im Size Inspector vertraut. Hier wird sichtbar, dass der Layout-Mechanismus von iOS auf dem Lösen von Gleichungssystemen basiert:

    Layout Constraints Size Inspector

    Hinweis: Die Reihenfolge von First und Second Item sind beliebig vertauschbar, da die Gleichungen entsprechend umgestellt werden können. Im Auswahlmenü finden Sie dafür auch eine Option:

    Reverse First And Second Item

Tutorial 2: Layout-Constraints automatisch erstellen

In den folgenden Tutorials soll die Darstellung der Lernkarte so mit Layout-Constraints versehen werden, dass die Darstellung für die jeweilige Bildschirmgröße und -drehung angepasst wird:

Ergebnisse: Layout-Constraint Tutorial

  1. Laden Sie den Start-Stand von dem Flashcards-Beispielprojekt: Flashcards.zip.

  2. Öffnen Sie Main.storyboard und machen Sie sich mit den Layout-Buttons rechts unten vertraut:

    Layout-Buttons im Interface-Builder

  3. Wählen Sie im Storyboard den CardViewController aus und prüfen Sie, dass eine Anordnung wie oben abgebildet gegeben ist.

  4. Wählen Sie Resolve Auto Layout Issues » All Views » Reset to Suggested Constraints um testweise automatisch Layout-Constraints für das View zu erstellen:

    Interface Builder: Reset to suggested constraints
  5. Prüfen Sie die angelegten Constraints.

  6. Blenden Sie mit View as: ... die Größenauswahl für das Storyboard ein und prüfen Sie das automatisch erstellte Layout für verschiedene iPhone-Größen und Rotationen:

    Xcode Storyboard Preview
  7. Wählen Sie Resolve Auto Layout Issues » All Views » Clear Constraints um die Constraints wieder zu entfernen - die Constraints für das View werden im Folgenden manuell erstellt:

    Resolve Auto Layout Issues » All Views » Clear Constraints

Tutorial 3: Views mit Layout Constraints positionieren

  1. Ziehen Sie schräg vom Textfeld auf das umgebende View und erstellen Sie einen Equal Height Constraint, um die Größe des Textfeldes abhängig von dem zur Verfügung stehenden Platz zu machen:

    Equal Height Constraint für Textfeld erstellen

    Der erstellte Constraint wird rot dargestellt, weil die Constraints für das Textfeld noch keine vollständige Positionierung ergeben.

  2. Wählen Sie den Equal Height-Constraint aus.
    Prüfen Sie die Reihenfolge der Views im Constraint: First Item sollte das Text Label sein und Second Item das Superview. Wählen Sie andernfalls im Auswahlmenü Reverse First and Second Item.
    Geben Sie als Constant 0 und als Multiplier 1:3 an, um dem Textfeld ein Drittel der Höhe des Controller-Views zuzuweisen:

    Equal Height Constraint bearbeiten

    Damit muss die Höhe des Text-Labels immer ein Drittel der Höhe des beinhaltenden Views sein:

    Text Label.Height = ⅓ * Superview.Height + 0

  3. Verwenden Sie Add New Constraints um das Textfeld auf eine feste Breite von 275 Punkt zu konfigurieren und erstellen Sie mit Align zwei neue Constraints Horizontally + Vertically in Container für das Textfeld:

    Add New Constraints
    Horizontally + Vertically in Container
  4. Wählen Sie den Center Y Constraint aus. Prüfen Sie auch hier die Reihenfolge der Constraint-Items und konfigurieren Sie einen Multiplier von 3:4 um das Textfeld anteilig oberhalb im Hintergrund-View zu platzieren:

    Center Y Constraint bearbeiten
  5. Wählen Sie den Flip-Button aus und erstellen Sie mittels Align einen Center Horizontally in Container-Constraint:

    Center Horizontally in Container
  6. Wählen Sie die drei Buttons aus und verwenden Sie Align um ein Vertical Centers Constraint zu erstellen:

  7. Verwenden Sie Add Constraints um Abstände von 20 Punkt nach links und rechts und 60 Punkt nach unten zu erstellen:

  8. Prüfen Sie die Constraints für verschiedene Bildschirmgrößen mit der Vorschau im Assistant Editor:

    Vorschau für verschiedene Bildschirmgrößen
  9. Starten Sie die App im Simulator und drehen Sie den Simulator mittels Hardware » Rotate Left/Right ⌘◀/▶

  10. Committen Sie Ihre Änderungen: „13. Größenunabhängiges Layout für CardViewController“.