Biorąc pod uwagę ten kod:
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
Wynika z tego interfejs:
Jak mogę VStack
wypełnić całą szerokość ekranu, nawet jeśli etykiety / komponenty tekstowe nie wymagają pełnej szerokości?
Sztuczka, którą znalazłem, polega na wstawieniu pustego HStack
elementu do struktury w następujący sposób:
VStack(alignment: .leading) {
HStack {
Spacer()
}
Text("Title")
.font(.title)
Text("Content")
.lineLimit(nil)
.font(.body)
Spacer()
}
Który daje pożądany projekt:
Czy jest lepszy sposób?
Odpowiedzi:
Spróbuj użyć modyfikatora .frame z następującymi opcjami:
.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) } }
Jest to opisywane jako elastyczna ramka ( patrz dokumentacja ), która rozciąga się, aby wypełnić cały ekran, a gdy ma dodatkową przestrzeń, wyśrodkuje zawartość wewnątrz niej.
źródło
.leading
)..background(Color.red)
.backgound(Color.red)
to tło widoku ramki, ale nie tło tego,VStack
co jest w widoku ramki. (Przyczyna tego jest wyjaśniona w filmach WWDC: P). Jeśli umieścisz go.background(Color.green)
przed.frame
, zobaczyszVStack
, że klatka ma szerokość, która pasuje do jej zawartości, podczas gdy ramka ma czerwone tło ... Kiedy wstawisz.frame(...)
, nie edytujeszVStack
, w rzeczywistości tworzy inny widok.Alternatywny układ układania, który działa i być może nieco bardziej intuicyjny, to:
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) } }
Zawartość można również łatwo zmienić położenie, usuwając je
Spacer()
, jeśli to konieczne.źródło
Jest lepszy sposób!
Aby
VStack
wypełnić szerokość jego rodzica, możesz użyćGeometryReader
i ustawić ramkę. (.relativeWidth(1.0)
powinno działać, ale najwyraźniej teraz nie)struct ContentView : View { var body: some View { GeometryReader { geometry in VStack { Text("test") } .frame(width: geometry.size.width, height: nil, alignment: .topLeading) } } }
Aby uzyskać
VStack
szerokość rzeczywistego ekranu, możesz użyćUIScreen.main.bounds.width
podczas ustawiania ramki zamiast używaćGeometryReader
, ale wyobrażam sobie, że prawdopodobnie chciałeś szerokości widoku rodzica.Ponadto ten sposób ma dodatkową zaletę polegającą na tym, że nie dodaje się odstępów w twoim,
VStack
co może się zdarzyć (jeśli masz odstępy), jeśli dodałeśHStack
z aSpacer()
jako zawartość doVStack
.AKTUALIZACJA - NIE MA LEPSZEGO SPOSOBU!
Po sprawdzeniu zaakceptowanej odpowiedzi zdałem sobie sprawę, że zaakceptowana odpowiedź tak naprawdę nie działa! Na pierwszy rzut oka wygląda na to, że działa, ale jeśli zaktualizujesz go,
VStack
aby miał zielone tło, zauważysz, żeVStack
ma nadal tę samą szerokość.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) } } }
Dzieje się tak, ponieważ w
.frame(...)
rzeczywistości dodaje kolejny widok do hierarchii widoków i ten widok kończy się wypełnieniem ekranu. JednakVStack
nadal nie.Ta kwestia również wydaje się być taka sama w mojej odpowiedzi i można ją sprawdzić, używając tego samego podejścia, co powyżej (umieszczając różne kolory tła przed i po
.frame(...)
. Jedynym sposobem, który wydaje się faktycznie poszerzać,VStack
jest użycie odstępników: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) } }
źródło
relativeWidth
, nie działał. JakośGeometryReader
działa, jeśli.frame
zostanie umieszczony wcześniej.background(Color.red)
GeometryReader
na liście wyświetlanej, gdy „Dodaj modyfikator” po IShow SwiftUI Inspector
lub w jakimkolwiek innym miejscu w interfejsie użytkownika. Jak odkryłeśGeometryReader
?Dzięki Swift 5.2 i iOS 13.4, w zależności od potrzeb, możesz użyć jednego z poniższych przykładów, aby dopasować swoje
VStack
do najważniejszych wiodących ograniczeń i pełnowymiarowej ramki.Zwróć uwagę, że poniższe fragmenty kodu powodują ten sam ekran, ale nie gwarantują efektywnej klatki
VStack
ani liczbyView
elementów, które mogą pojawić się podczas debugowania hierarchii widoków.1. Za pomocą
frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)
metodyNajprostszym podejściem jest ustawienie ramy
VStack
z maksymalną szerokością i wysokością, a także przejście wymaganego wyrównania wframe(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) } }
Alternatywnie, jeśli ustawienie maksymalnej klatki z określonym wyrównaniem dla twojego
View
s jest częstym wzorcem w twoim kodzie, możesz utworzyćView
dla niego metodę rozszerzenia :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. Użycie
Spacer
s do wymuszenia wyrównaniaMożesz osadzić swoje
VStack
wnętrze w pełnym rozmiarzeHStack
i użyć końcowych i dolnych,Spacer
aby wymusićVStack
wyrównanie górnego wiodącego: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. Używając
ZStack
ai pełnowymiarowego tłaView
Ten przykład pokazuje, jak osadzić
VStack
wewnątrz a,ZStack
które ma górne wyrównanie wiodące. Zwróć uwagę, jakColor
widok jest używany do ustawiania maksymalnej szerokości i wysokości: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. Korzystanie
GeometryReader
GeometryReader
posiada następującą deklarację :Poniższy fragment kodu pokazuje, jak używać go
GeometryReader
do wyrównywaniaVStack
z najważniejszymi wiodącymi ograniczeniami i pełnowymiarową ramką: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. Za pomocą
overlay(_:alignment:)
metodyJeśli chcesz wyrównać swoje
VStack
z najważniejszymi wiodącymi ograniczeniami na szczycie istniejącego pełnego rozmiaruView
, możesz użyćoverlay(_:alignment:)
metody: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 ) } }
Pokaz:
źródło
Najprostszym sposobem, w jaki udało mi się rozwiązać ten problem, było użycie ZStack + .edgesIgnoringSafeArea (.all)
struct TestView : View { var body: some View { ZStack() { Color.yellow.edgesIgnoringSafeArea(.all) VStack { Text("Hello World") } } } }
źródło
Użyj tego
źródło
Możesz to zrobić za pomocą
GeometryReader
GeometryReader
Kod:
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) } } } }
Twoje wyjście, takie jak:
źródło
Dobre rozwiązanie i bez „urządzeń” jest zapomniane
ZStack
ZStack(alignment: .top){ Color.red VStack{ Text("Hello World").font(.title) Text("Another").font(.body) } }
Wynik:
źródło
Jeszcze jedną alternatywą jest umieszczenie jednego z widoków podrzędnych wewnątrz an
HStack
i umieszczenieSpacer()
po nim: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) } }
w wyniku:
źródło
To zadziałało w moim przypadku (
ScrollView
(opcjonalnie), więc w razie potrzeby można dodać więcej treści, a także zawartość wyśrodkowaną):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
źródło
Wiem, że to nie zadziała dla wszystkich, ale pomyślałem, że interesujące jest to, że samo dodanie dzielnika rozwiązuje ten problem.
struct DividerTest: View { var body: some View { VStack(alignment: .leading) { Text("Foo") Text("Bar") Divider() }.background(Color.red) } }
źródło
Oto przydatny fragment kodu:
extension View { func expandable () -> some View { ZStack { Color.clear self } } }
Porównaj wyniki
.expandable()
zi bez modyfikatora:Text("hello") .background(Color.blue)
-
Text("hello") .expandable() .background(Color.blue)
źródło
Projekt strony logowania przy użyciu
SwiftUI
import SwiftUI struct ContentView: View { @State var email: String = "[email protected]" @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() } }
źródło
Możesz użyć GeometryReader w poręcznym rozszerzeniu, aby wypełnić element nadrzędny
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) } } }
więc korzystając z żądanego przykładu, otrzymasz
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) } }
(uwaga, element dystansowy nie jest już potrzebny)
źródło
var body: some View { VStack { CarouselView().edgesIgnoringSafeArea(.all) List { ForEach(viewModel.parents) { k in VideosRowView(parent: k) } } } }
źródło