Lernziel
Nach dieser Lektion kannst du Abstände mit padding gezielt setzen, Views mit frame eine feste, minimale oder maximale Größe geben, sie mit alignment an jede Stelle eines Bereichs schieben und mit dem sicheren Bereich des Bildschirms umgehen.
Das Problem: Fast richtig ist nicht richtig
Mit Stacks aus der letzten Lektion kannst du Views anordnen. Aber dann kommen die Feinheiten:
- Der Titel soll links am Bildschirmrand stehen, nicht in der Mitte.
- Ein Button soll die ganze Breite einnehmen.
- Zwischen Inhalt und Rand soll links und rechts mehr Luft sein als oben und unten.
- Ein Hintergrundbild soll bis ganz an den Rand gehen, auch hinter die Statusleiste.
Für all das gibt es drei Werkzeuge: padding, frame und alignment.
padding: Luft schaffen
Du kennst .padding() schon. Ohne Angaben fügt es auf allen vier Seiten einen Standardabstand hinzu. Du kannst es aber genau steuern:
Text("Alle Seiten 20").padding(20)
Text("Nur oben").padding(.top)
Text("Links und rechts").padding(.horizontal, 24)
Text("Oben und unten").padding(.vertical, 8)
Text("Unten und rechts").padding([.bottom, .trailing], 12)| Angabe | Seiten |
|---|---|
.top, .bottom, .leading, .trailing | eine Seite |
.horizontal | links und rechts |
.vertical | oben und unten |
.all | alle (Standard) |
[.top, .leading] | mehrere als Array |
Mehrere padding-Modifier hintereinander addieren sich:
Text("Button")
.padding(.horizontal, 24)
.padding(.vertical, 12)Tipp
Bleib bei einer kleinen Auswahl an Abständen, zum Beispiel 4, 8, 12, 16, 24 und 32. Ein Layout mit einheitlichen Abständen wirkt sofort ruhiger und professioneller als eines, in dem überall andere Werte stehen. In Lektion 3.29 legst du dafür eigene Konstanten an.
frame: Größe festlegen
Feste Größe
Text("Fest")
.frame(width: 200, height: 60)
.background(.yellow)Der gelbe Bereich ist genau 200 × 60 Punkte groß, und der Text steht in seiner Mitte. Du kannst auch nur eine der beiden Angaben machen, etwa .frame(height: 60).
Wichtig zu verstehen: .frame ändert nicht die Größe des Textes. Es legt einen Rahmen um ihn, und der Text wird darin platziert. Deshalb kommt .background hier nach .frame (Lektion 2.4).
Flexible Größe mit maxWidth
Eine der wichtigsten Zeilen in SwiftUI überhaupt:
Text("Volle Breite")
.frame(maxWidth: .infinity)
.padding()
.background(.blue)
.foregroundStyle(.white)maxWidth: .infinity heißt: „Werde so breit, wie du darfst.“ Der Rahmen dehnt sich über die ganze verfügbare Breite aus. So baust du zum Beispiel Buttons über die volle Breite.
Ebenso gibt es maxHeight: .infinity und die Gegenstücke minWidth und minHeight:
Text("Mindestens 44 hoch")
.frame(minHeight: 44)Tipp
Apple empfiehlt, dass antippbare Elemente mindestens 44 × 44 Punkte groß sind. Kleinere Ziele sind schwer zu treffen. .frame(minWidth: 44, minHeight: 44) sorgt dafür.
alignment: Position im Rahmen
Ein frame kann seinen Inhalt auch ausrichten. Damit löst du das Problem aus der letzten Lektion:
VStack(alignment: .leading) {
Text("Überschrift")
.font(.title)
Text("Untertitel")
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()Erst maxWidth: .infinity macht den Rahmen bildschirmbreit. Dann schiebt alignment: .leading den Inhalt an den linken Rand. Ohne maxWidth hätte das alignment keinen Platz, in dem es etwas verschieben könnte.
Erwartetes Ergebnis
Überschrift und Untertitel stehen links, mit Standardabstand zum Rand.
Die möglichen Werte kennst du vom ZStack: .topLeading, .top, .topTrailing, .leading, .center, .trailing, .bottomLeading, .bottom, .bottomTrailing.
Text("Unten rechts")
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing)
.padding()Spacer oder frame?
Beides kann Views an den Rand schieben. HStack { Text("A"); Spacer() } und Text("A").frame(maxWidth: .infinity, alignment: .leading) sehen gleich aus. Das frame-Muster ist kürzer und braucht keinen zusätzlichen Stack. Spacer sind praktischer, wenn zwischen mehreren Elementen Platz verteilt werden soll.
Gleich breite Elemente
Erinnerst du dich an die Statistik-Kacheln aus Übung 2 der letzten Lektion? Sie waren unterschiedlich breit, weil jede nur so breit wie ihr Inhalt war. Mit maxWidth: .infinity an jeder Kachel teilen sie sich den Platz gleichmäßig:
HStack(spacing: 12) {
Text("128")
.frame(maxWidth: .infinity)
.padding()
.background(.yellow)
Text("4.200")
.frame(maxWidth: .infinity)
.padding()
.background(.yellow)
}
.padding()Weil beide gleich flexibel sind, bekommen sie gleich viel Platz.
Der sichere Bereich
Ein iPhone-Bildschirm hat Zonen, in denen Inhalt schlecht aufgehoben ist: oben die Statusleiste und die Dynamic Island, unten der Home-Balken. Den Bereich dazwischen nennt man Safe Area, den sicheren Bereich.
SwiftUI hält deinen Inhalt automatisch in der Safe Area. Das ist fast immer richtig. Für Hintergründe willst du aber oft, dass sie bis ganz an den Rand gehen:
ZStack {
Color.indigo
.ignoresSafeArea()
Text("Willkommen")
.font(.largeTitle)
.foregroundStyle(.white)
}Erwartetes Ergebnis
Der ganze Bildschirm ist indigoblau, auch hinter der Statusleiste. Der Text steht trotzdem sicher in der Mitte.
Nur der Hintergrund ignoriert die Safe Area, der Text bleibt brav darin. Genau so sollte es sein.
Achtung
Wende .ignoresSafeArea() nur auf Hintergründe an, nie auf Texte oder Buttons. Sonst können sie hinter der Dynamic Island oder dem Home-Balken verschwinden.
Eine elegante Alternative ist .background mit einer Farbe. Sie dehnt sich automatisch in die Safe Area aus:
VStack {
Text("Inhalt")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.indigo)Ein vollständiges Beispiel
Ein typischer Willkommensbildschirm, der alles aus dieser und der letzten Lektion verbindet:
struct WelcomeView: View {
var body: some View {
VStack(spacing: 24) {
Spacer()
Image(systemName: "sparkles")
.font(.system(size: 72))
.foregroundStyle(.yellow)
VStack(spacing: 8) {
Text("Willkommen")
.font(.largeTitle)
.bold()
Text("Schön, dass du da bist. Los geht's mit deiner ersten Gewohnheit.")
.multilineTextAlignment(.center)
.foregroundStyle(.secondary)
}
Spacer()
Text("Starten")
.font(.headline)
.foregroundStyle(.white)
.frame(maxWidth: .infinity)
.padding()
.background(.blue)
.clipShape(.rect(cornerRadius: 14))
}
.padding(.horizontal, 24)
.padding(.bottom)
}
}Erwartetes Ergebnis
Oben und unten verteilter Leerraum, in der Mitte Symbol, Titel und Text, ganz unten ein blauer „Starten“-Knopf über die volle Breite.
Der „Button“ ist hier noch ein gestalteter Text. Wie man echte Buttons baut, lernst du in Lektion 2.11.
Typische Fehler
alignment ohne maxWidth
Text("Links?")
.frame(alignment: .leading)Nichts passiert. Der Rahmen ist so groß wie der Text, also gibt es keinen Platz zum Verschieben. Ergänze maxWidth: .infinity.
background vor frame
Text("Volle Breite?")
.background(.blue)
.frame(maxWidth: .infinity)Nur der Text ist blau, nicht die ganze Breite. Der Hintergrund gehört nach den Rahmen.
Feste Größen überall
.frame(width: 375)Auf einem kleineren iPhone ragt das über den Rand, auf einem größeren bleibt Platz frei. Feste Breiten sind selten eine gute Idee. Nutze maxWidth: .infinity und padding, dann passt sich dein Layout jedem Gerät an.
Inhalt hinter der Dynamic Island
.ignoresSafeArea() wurde auf die ganze View statt nur auf den Hintergrund angewendet. Verschieb den Modifier auf die Farbe oder das Hintergrundbild.
FAQ
Anfänger
Was bedeutet das überhaupt?Warum sind meine Views alle in der Mitte?
Weil SwiftUI Views standardmäßig zentriert. Das ist kein Fehler, sondern die Grundeinstellung. Mit frame(maxWidth: .infinity, alignment: ...) oder Spacern bestimmst du die Position selbst.
Gibt es auch negative Abstände?
.padding(-10) funktioniert technisch, ist aber selten eine gute Idee. Willst du Elemente überlappen lassen, sind ZStack, .overlay oder .offset die klareren Werkzeuge.
Wie mache ich eine View genau halb so breit wie den Bildschirm?
Dafür gibt es .containerRelativeFrame(.horizontal) { width, _ in width / 2 }. In vielen Fällen ist aber ein HStack mit zwei gleich flexiblen Elementen einfacher.
Fortgeschritten
Warum verhält es sich so?Was macht fixedSize()?
Es sagt einer View: „Nimm genau die Größe, die du idealerweise brauchst, egal was dir angeboten wird.“ Praktisch, wenn ein Text nicht abgeschnitten werden soll: .fixedSize(horizontal: false, vertical: true) lässt ihn in der Höhe wachsen, statt ihn zu kürzen.
Was ist der Unterschied zwischen padding und safeAreaPadding?
.safeAreaPadding erweitert den sicheren Bereich, statt einfach Abstand hinzuzufügen. Das ist vor allem bei scrollbaren Inhalten nützlich: Der Inhalt startet eingerückt, darf beim Scrollen aber bis an den Rand. Mehr in Lektion 3.18.
Profi
Was passiert unter der Haube?Wie funktioniert frame im Layout-Prozess?
frame ist selbst eine View. Sie nimmt den Größenvorschlag der Eltern entgegen, passt ihn an ihre Grenzen an (feste Größe, min/max) und gibt diesen veränderten Vorschlag an ihr Kind weiter. Dann wählt sie ihre eigene Größe innerhalb ihrer Grenzen und platziert das Kind gemäß alignment. Deshalb kann ein frame größer sein als sein Inhalt.
Kann ich eigene Ausrichtungen definieren?
Ja, mit AlignmentID kannst du eigene Ausrichtungslinien anlegen, etwa um Elemente in verschiedenen Stacks aneinander auszurichten. Das braucht man selten, aber für komplexe Formulare ist es sehr mächtig.
Übungen
Übung 1: Linksbündiger Titel
Baue einen Bildschirmkopf mit einem großen Titel „Einstellungen“ und einem kleinen Untertitel darunter. Beide sollen links stehen, mit 20 Punkten Abstand zum linken Rand und 40 Punkten zum oberen.
Lösung Übung 1Erst selbst probieren!
VStack(alignment: .leading, spacing: 4) {
Text("Einstellungen")
.font(.largeTitle)
.bold()
Text("Passe die App an deine Wünsche an")
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 20)
.padding(.top, 40)
.frame(maxHeight: .infinity, alignment: .top)Der zweite frame schiebt den ganzen Block nach oben, statt ihn vertikal zu zentrieren.
Übung 2: Zwei Buttons unten
Baue am unteren Bildschirmrand zwei gleich breite „Buttons“ nebeneinander: „Abbrechen“ (grau) und „Speichern“ (blau). Beide haben abgerundete Ecken, 12 Punkte Abstand dazwischen und 16 Punkte Abstand zum Rand.
Lösung Übung 2Erst selbst probieren!
VStack {
Spacer()
HStack(spacing: 12) {
Text("Abbrechen")
.frame(maxWidth: .infinity)
.padding()
.background(Color(.systemGray5))
.clipShape(.rect(cornerRadius: 12))
Text("Speichern")
.foregroundStyle(.white)
.frame(maxWidth: .infinity)
.padding()
.background(.blue)
.clipShape(.rect(cornerRadius: 12))
}
.font(.headline)
.padding(16)
}Übung 3: Vollbild-Hero
Baue einen Bildschirm, dessen obere Hälfte ein Farbverlauf ist, der bis hinter die Statusleiste reicht, mit einem großen weißen Titel unten links darin. Die untere Hälfte enthält normalen Text auf dem Systemhintergrund.
Lösung Übung 3Erst selbst probieren!
VStack(spacing: 0) {
Text("Wien")
.font(.largeTitle)
.bold()
.foregroundStyle(.white)
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading)
.background(.purple.gradient)
Text("Die Hauptstadt Österreichs liegt an der Donau und ist für Kaffeehäuser, Musik und Architektur bekannt.")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}Beide Hälften sind gleich flexibel und teilen sich die Höhe. Der Verlauf als .background reicht automatisch bis hinter die Statusleiste.
Zusammenfassung
paddingschafft Abstand, auf allen oder bestimmten Seiten.framelegt einen Rahmen um eine View: fest, minimal oder maximal..frame(maxWidth: .infinity)macht eine View so breit wie möglich.alignmentimframebestimmt, wo der Inhalt im Rahmen sitzt. Es braucht dafür Platz.- Der sichere Bereich schützt Inhalte vor Dynamic Island und Home-Balken.
.ignoresSafeArea()nur für Hintergründe. - Feste Größen vermeiden, flexible Layouts bevorzugen.
Spickzettel
.padding() .padding(.horizontal, 24)
.padding([.top, .leading], 8)
.frame(width: 200, height: 60)
.frame(maxWidth: .infinity)
.frame(maxWidth: .infinity, alignment: .leading)
.frame(minWidth: 44, minHeight: 44)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
Color.blue.ignoresSafeArea()