Lassen Sie einen VStack die Breite des Bildschirms in SwiftUI ausfüllen


111

Angesichts dieses Codes:

import SwiftUI

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)

            Text("Content")
                .lineLimit(nil)
                .font(.body)

            Spacer()
        }
        .background(Color.red)
    }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

Daraus ergibt sich diese Wechselwirkung:

Vorschau

Wie kann ich VStackdie Breite des Bildschirms ausfüllen, auch wenn die Beschriftungen / Textkomponenten nicht die volle Breite benötigen?

Ein Trick, den ich gefunden habe, besteht darin, ein Leerzeichen HStack wie folgt in die Struktur einzufügen :

VStack(alignment: .leading) {
    HStack {
        Spacer()
    }
    Text("Title")
        .font(.title)

    Text("Content")
        .lineLimit(nil)
        .font(.body)

    Spacer()
}

Welches ergibt das gewünschte Design:

gewünschte Ausgabe

Gibt es einen besseren Weg?


1
Siehe meine Antwort , die bis zu 5 alternative Möglichkeiten zeigt, um ein ähnliches Ergebnis zu erzielen .
Imanou Petit

Antworten:


183

Versuchen Sie, den Modifikator .frame mit den folgenden Optionen zu verwenden:

.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Hello World").font(.title)
            Text("Another").font(.body)
            Spacer()
        }.frame(minWidth: 0,
                maxWidth: .infinity,
                minHeight: 0,
                maxHeight: .infinity,
                alignment: .topLeading
        ).background(Color.red)
    }
}

Dies wird als flexibler Rahmen beschrieben ( siehe Dokumentation ), der sich ausdehnt, um den gesamten Bildschirm auszufüllen, und wenn er zusätzlichen Platz hat, zentriert er seinen Inhalt darin.


Die Unteransichten werden nicht nach links ausgerichtet ( .leading). Nimmt die volle Breite ein, wenn vor.background(Color.red)
ielyamani

Code hinzugefügt, um auch links auszurichten. Sollte genau jetzt
passen

9
Das ist falsch. Dies .backgound(Color.red)ist der Hintergrund der Rahmenansicht, jedoch nicht der Hintergrund VStackder Rahmenansicht. (Der Grund dafür wird in den WWDC-Videos erklärt: P). Wenn Sie .background(Color.green)vor das setzen, sehen .frameSie, dass das VStackStandbild eine Breite hat, die zu seinem Inhalt passt, während der Rahmen einen roten Hintergrund hat ... Wenn Sie das setzen .frame(...), wird das nicht bearbeitet VStack, sondern es wird tatsächlich eine andere Ansicht erstellt.
Jake

2
@ Jake scheint wie ein Rezept für eine Katastrophe
Sudara

Haben Sie eine Frage: Warum Ausrichtung verwenden: .topLeading, da wir VStack haben (Ausrichtung: .leading)? Danke fürs Teilen
UIChris

35

Eine alternative Stapelanordnung, die funktioniert und vielleicht etwas intuitiver ist, ist die folgende:

struct ContentView: View {
    var body: some View {
        HStack() {
            VStack(alignment: .leading) {
                Text("Hello World")
                    .font(.title)
                Text("Another")
                    .font(.body)
                Spacer()
            }
            Spacer()
        }.background(Color.red)
    }
}

Der Inhalt kann bei Bedarf auch einfach neu positioniert werden, indem die entfernt Spacer()werden.

Hinzufügen / Entfernen von Abstandshaltern zum Ändern der Position


4
Gute Alternative ik Gefiel das GIF
ielyamani

1
Ich finde dies sowohl intuitiver als auch besser auf die Frage von OP abgestimmt, wonach das Stack-Element seinen Container füllen soll, anstatt einen Container um das Element zu legen.
brotskydotcom

1
Ich finde, dass dies die beste Antwort ist, die mit der Idee hinter SwiftUI
krummens

1
Definitiv die schnellste Antwort hier für ein so gemeinsames Layout.
dead_can_dance

30

Es gibt einen besseren Weg!

Um die VStackFüllung auf die Breite des übergeordneten Elements zu bringen, können Sie a verwenden GeometryReaderund den Rahmen festlegen. ( .relativeWidth(1.0)sollte funktionieren, aber anscheinend gerade nicht)

struct ContentView : View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("test")
            }
                .frame(width: geometry.size.width,
                       height: nil,
                       alignment: .topLeading)
        }
    }
}

Um VStackdie Breite des tatsächlichen Bildschirms zu bestimmen, können Sie UIScreen.main.bounds.widthbeim Festlegen des Rahmens anstelle von a verwenden GeometryReader, aber ich kann mir vorstellen, dass Sie wahrscheinlich die Breite der übergeordneten Ansicht wollten.

Diese Methode hat auch den zusätzlichen Vorteil VStack, dass Sie keinen Abstand hinzufügen, was passieren kann (wenn Sie einen Abstand haben), wenn Sie einen HStackmit einem Spacer()Inhalt als Inhalt zum hinzufügen VStack.

UPDATE - ES GIBT KEINEN BESSEREN WEG!

Nachdem ich die akzeptierte Antwort überprüft hatte, stellte ich fest, dass die akzeptierte Antwort nicht wirklich funktioniert! Es scheint auf den ersten Blick zu funktionieren, aber wenn Sie das aktualisieren VStack, um einen grünen Hintergrund zu erhalten, werden Sie feststellen, dass VStackes immer noch dieselbe Breite hat.

struct ContentView : View {
    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                Text("Hello World")
                    .font(.title)
                Text("Another")
                    .font(.body)
                Spacer()
                }
                .background(Color.green)
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
                .background(Color.red)
        }
    }
}

Dies liegt daran .frame(...), dass der Ansichtshierarchie tatsächlich eine weitere Ansicht hinzugefügt wird und diese Ansicht den Bildschirm ausfüllt. Das VStacktut das aber immer noch nicht.

Dieses Problem scheint auch in meiner Antwort dasselbe zu sein und kann mit demselben Ansatz wie oben überprüft werden (wobei verschiedene Hintergrundfarben vor und nach dem gesetzt werden .frame(...). Die einzige Möglichkeit, die tatsächlich zu erweitern scheint, VStackist die Verwendung von Abstandshaltern:

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack{
                Text("Title")
                    .font(.title)
                Spacer()
            }
            Text("Content")
                .lineLimit(nil)
                .font(.body)
            Spacer()
        }
            .background(Color.green)
    }
}

Hatte es versucht relativeWidth, hat nicht funktioniert. Funktioniert irgendwie, GeometryReaderwenn .framevor.background(Color.red)
ielyamani

Ich kann GeometryReaderin der angezeigten Liste nicht sehen , wenn ich "Modifikator hinzufügen" nach I Show SwiftUI Inspectoroder irgendwo anders in der Benutzeroberfläche. Wie haben Sie entdeckt GeometryReader?
gegangen

1
@gone - Stack Overflow :)
Jake

17

Mit Swift 5.2 und iOS 13.4 können Sie je nach Bedarf eines der folgenden Beispiele verwenden, um Ihre Anforderungen an die wichtigsten VStackEinschränkungen und einen Rahmen in voller Größe anzupassen.

Beachten Sie, dass die folgenden Codeausschnitte zu derselben Anzeige führen, jedoch weder den effektiven Frame VStacknoch die Anzahl der ViewElemente garantieren, die beim Debuggen der Ansichtshierarchie angezeigt werden.


1. frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)Methode verwenden

Der einfachste Ansatz besteht darin, den Rahmen Ihres VStackmit maximaler Breite und Höhe einzustellen und die erforderliche Ausrichtung zu übergeben in frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:):

struct ContentView: View {

    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)
            Text("Content")
                .font(.body)
        }
        .frame(
            maxWidth: .infinity,
            maxHeight: .infinity,
            alignment: .topLeading
        )
        .background(Color.red)
    }

}

Wenn das Festlegen des maximalen Rahmens mit einer bestimmten Ausrichtung für Ihr Views ein häufiges Muster in Ihrer Codebasis ist, können Sie alternativ eine Erweiterungsmethode dafür erstellen View:

extension View {

    func fullSize(alignment: Alignment = .center) -> some View {
        self.frame(
            maxWidth: .infinity,
            maxHeight: .infinity,
            alignment: alignment
        )
    }

}

struct ContentView : View {

    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)
            Text("Content")
                .font(.body)
        }
        .fullSize(alignment: .topLeading)
        .background(Color.red)
    }

}

2. Verwenden Sie Spacers, um die Ausrichtung zu erzwingen

Sie können Ihr VStackInneres in voller Größe einbetten HStackund Trailing und Bottom Spacers verwenden, um Ihre VStackAusrichtung nach oben zu erzwingen :

struct ContentView: View {

    var body: some View {
        HStack {
            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)

                Spacer() // VStack bottom spacer
            }

            Spacer() // HStack trailing spacer
        }
        .frame(
            maxWidth: .infinity,
            maxHeight: .infinity
        )
        .background(Color.red)
    }

}

3. Verwenden Sie einen ZStackHintergrund in voller GrößeView

Dieses Beispiel zeigt, wie Sie Ihr VStackInneres einbetten ZStack, das eine Ausrichtung nach oben aufweist. Beachten Sie, wie in der ColorAnsicht die maximale Breite und Höhe festgelegt wird:

struct ContentView: View {

    var body: some View {
        ZStack(alignment: .topLeading) {
            Color.red
                .frame(maxWidth: .infinity, maxHeight: .infinity)

            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)
            }
        }
    }

}

4. Verwenden von GeometryReader

GeometryReaderhat die folgende Erklärung :

Eine Containeransicht, die ihren Inhalt als Funktion ihrer eigenen Größe und ihres Koordinatenraums definiert. [...] Diese Ansicht gibt eine flexible bevorzugte Größe für das übergeordnete Layout zurück.

Das folgende Code-Snippet zeigt, wie Sie es GeometryReaderan den wichtigsten VStackEinschränkungen und einem Rahmen in voller Größe ausrichten können :

struct ContentView : View {

    var body: some View {
        GeometryReader { geometryProxy in
            VStack(alignment: .leading) {
                Text("Title")
                    .font(.title)
                Text("Content")
                    .font(.body)
            }
            .frame(
                width: geometryProxy.size.width,
                height: geometryProxy.size.height,
                alignment: .topLeading
            )
        }
        .background(Color.red)
    }

}

5. overlay(_:alignment:)Methode verwenden

Wenn Sie Ihre VStackmit den wichtigsten Einschränkungen über einer vorhandenen Größe in voller Größe ausrichten möchten View, können Sie folgende overlay(_:alignment:)Methode verwenden:

struct ContentView: View {

    var body: some View {
        Color.red
            .frame(
                maxWidth: .infinity,
                maxHeight: .infinity
            )
            .overlay(
                VStack(alignment: .leading) {
                    Text("Title")
                        .font(.title)
                    Text("Content")
                        .font(.body)
                },
                alignment: .topLeading
            )
    }

}

Anzeige:


9

Der einfachste Weg, das Problem zu lösen, war die Verwendung eines ZStack + .edgesIgnoringSafeArea (.all).

struct TestView : View {

    var body: some View {
        ZStack() {
            Color.yellow.edgesIgnoringSafeArea(.all)
            VStack {
                Text("Hello World")
            }
        }
    }
}

4

Sie können es mit tun GeometryReader

GeometryReader

Code:

struct ContentView : View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
               Text("Turtle Rock").frame(width: geometry.size.width, height: geometry.size.height, alignment: .topLeading).background(Color.red)
            }
        }
    }
}

Ihre Ausgabe mag:

Geben Sie hier die Bildbeschreibung ein



4

Eine gute Lösung und ohne "Erfindungen" ist das Vergessene ZStack

ZStack(alignment: .top){
    Color.red
    VStack{
        Text("Hello World").font(.title)
        Text("Another").font(.body)
    }
}

Ergebnis:

Geben Sie hier die Bildbeschreibung ein


3

Eine weitere Alternative besteht darin, eine der Unteransichten innerhalb von a HStackund Spacer()danach zu platzieren:

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {

            HStack {
                Text("Title")
                    .font(.title)
                    .background(Color.yellow)
                Spacer()
            }

            Text("Content")
                .lineLimit(nil)
                .font(.body)
                .background(Color.blue)

            Spacer()
            }

            .background(Color.red)
    }
}

ergebend :

HStack in einem VStack


1
Imho, das ist die eleganteste Lösung
Vyacheslav

3

Dies hat bei mir funktioniert ( ScrollView(optional), sodass bei Bedarf weitere Inhalte hinzugefügt werden können, plus zentrierte Inhalte):

import SwiftUI

struct SomeView: View {
    var body: some View {
        GeometryReader { geometry in
            ScrollView(Axis.Set.horizontal) {
                HStack(alignment: .center) {
                    ForEach(0..<8) { _ in
                        Text("🥳")
                    }
                }.frame(width: geometry.size.width, height: 50)
            }
        }
    }
}

// MARK: - Preview

#if DEBUG
struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        SomeView()
    }
}
#endif

Ergebnis

zentriert


2

Ich weiß, dass dies nicht für alle funktionieren wird, aber ich fand es interessant, dass nur das Hinzufügen eines Teilers dies löst.

struct DividerTest: View {
var body: some View {
    VStack(alignment: .leading) {
        Text("Foo")
        Text("Bar")
        Divider()
    }.background(Color.red)
  }
}

2

Dies ist ein nützlicher Code:

extension View {
    func expandable () -> some View {
        ZStack {
            Color.clear
            self
        }
    }
}

Vergleichen Sie die Ergebnisse mit und ohne .expandable()Modifikator:

Text("hello")
    .background(Color.blue)

- -

Text("hello")
    .expandable()
    .background(Color.blue)

Geben Sie hier die Bildbeschreibung ein


2

Sie können GeometryReader in einer praktischen Erweiterung verwenden, um das übergeordnete Element zu füllen

extension View {
    func fillParent(alignment:Alignment = .center) -> some View {
        return GeometryReader { geometry in
            self
                .frame(width: geometry.size.width,
                       height: geometry.size.height,
                       alignment: alignment)
        }
    }
}

Wenn Sie also das angeforderte Beispiel verwenden, erhalten Sie

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Title")
                .font(.title)

            Text("Content")
                .lineLimit(nil)
                .font(.body)
        }
        .fillParent(alignment:.topLeading)
        .background(Color.red)
    }
}

(Beachten Sie, dass der Abstandshalter nicht mehr benötigt wird.)

Geben Sie hier die Bildbeschreibung ein


2

Geben Sie hier die Bildbeschreibung ein

Anmeldeseitengestaltung mit SwiftUI

import SwiftUI

struct ContentView: View {

    @State var email: String = "william@gmail.com"
    @State var password: String = ""
    @State static var labelTitle: String = ""


    var body: some View {
        VStack(alignment: .center){
            //Label
            Text("Login").font(.largeTitle).foregroundColor(.yellow).bold()
            //TextField
            TextField("Email", text: $email)
                .textContentType(.emailAddress)
                .foregroundColor(.blue)
                .frame(minHeight: 40)
                .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.green))

            TextField("Password", text: $password) //Placeholder
                .textContentType(.newPassword)
                .frame(minHeight: 40)
                .foregroundColor(.blue) // Text color
                .background(RoundedRectangle(cornerRadius: 10).foregroundColor(Color.green))

            //Button
            Button(action: {

            }) {
                HStack {
                    Image(uiImage: UIImage(named: "Login")!)
                        .renderingMode(.original)
                        .font(.title)
                        .foregroundColor(.blue)
                    Text("Login")
                        .font(.title)
                        .foregroundColor(.white)
                }


                .font(.headline)
                .frame(minWidth: 0, maxWidth: .infinity)
                .background(LinearGradient(gradient: Gradient(colors: [Color("DarkGreen"), Color("LightGreen")]), startPoint: .leading, endPoint: .trailing))
                .cornerRadius(40)
                .padding(.horizontal, 20)

                .frame(width: 200, height: 50, alignment: .center)
            }
            Spacer()

        }.padding(10)

            .frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity, minHeight: 0, idealHeight: .infinity, maxHeight: .infinity, alignment: .top)
            .background(Color.gray)

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.