SwiftUI'de bir VStack'in ekranın genişliğini doldurmasını sağlayın


111

Bu kod verildiğinde:

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

Bu etkileşimle sonuçlanır:

Ön izleme

VStackEtiketler / metin bileşenlerinin tam genişliğe ihtiyacı olmasa bile ekranın genişliğini nasıl doldurabilirim?

Bulduğum bir numara , yapıya şu şekilde bir boşluk eklemek HStack:

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

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

    Spacer()
}

İstenilen tasarımı veren:

Istenilen çıktı

Daha iyi bir yol var mı?


1
Benzer bir sonuç elde etmenin 5 alternatif yolunu gösteren yanıtlayıcıma bakın .
Imanou Petit

Yanıtlar:


183

Aşağıdaki seçeneklerle .frame değiştiriciyi kullanmayı deneyin:

.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)
    }
}

Bu, tüm ekranı dolduracak şekilde genişleyen esnek bir çerçeve ( belgelere bakın ) olarak tanımlanır ve fazladan alan olduğunda içeriğini onun içinde ortalayacaktır.


Alt görünümler sola ( .leading) hizalanmaz . .background(Color.red)
Önceden

Sola hizalamak için kod eklendi. Şimdi tam olarak eşleşmeli
svarrall

9
Bu yanlış. .backgound(Color.red)Çerçevenin görünümünün arka plan, ancak arka planı olan VStackkare görünümünde olduğunu. (Bunun nedeni WWDC videolarında açıklanmıştır: P). Eğer koyarsanız .background(Color.green)önce .framegöreceksiniz VStacksen koyduğunuzda ... hala çerçeve kırmızı arka planı sahipken içeriğini uygun bir genişliğe sahiptir .frame(...), bu düzenleme değil VStack, aslında başka bir görünüm yaratıyor.
Jake

2
@Jake bir felaket için bir reçete gibi görünüyor
Sudara

Bir sorunuz var tho: Neden hizalamayı kullanmalıyız: .topLeading çünkü VStack (hizalama: .leading) ??
Paylaştığınız

35

İşe yarayan ve belki biraz daha sezgisel olan alternatif bir istifleme düzeni şudur:

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)
    }
}

İçerik, Spacer()gerekirse 'ler kaldırılarak da kolayca yeniden konumlandırılabilir .

Konum değiştirmek için Aralayıcı ekleme / çıkarma


4
🏻Liked the GIF
ielyamani

1
Bunu hem daha sezgisel buluyorum hem de OP'nin sorusuyla daha uyumlu buluyorum, bu soruyu, öğenin etrafına bir konteyneri nasıl yerleştireceğimi değil, Stack öğesinin kabını nasıl dolduracağını düşündüm.
brotskydotcom

1
Bunu, SwiftUI'nin arkasındaki fikirle uyumlu en iyi cevap olarak görüyorum
krummens

1
Böylesine yaygın bir düzen için kesinlikle en SwiftUI-y cevabı burada.
dead_can_dance

30

Daha iyi bir yol var!

Yapmak için VStackdoldurunuz, üstünün genişliği bir kullanabilir GeometryReaderve çerçeve ayarlayın. ( .relativeWidth(1.0)çalışmalı ama görünüşe göre şu anda çalışmıyor)

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

VStackGerçek ekranın genişliğini yapmak için UIScreen.main.bounds.widthçerçeveyi ayarlamak yerine a kullanmak yerine kullanabilirsiniz GeometryReader, ancak muhtemelen ana görünümün genişliğini istediğinizi tahmin ediyorum.

Ayrıca, bu yolun, içeriğinde VStackbir HStackile bir eklediyseniz (boşluk varsa) olabilecek boşluk eklememe avantajı vardır .Spacer()VStack

GÜNCELLEME - DAHA İYİ BİR YOL YOK!

Kabul edilen cevabı kontrol ettikten sonra, kabul edilen cevabın aslında işe yaramadığını fark ettim! İlk bakışta işe yarıyor gibi görünüyor, ancak VStackyeşil bir arka plana sahip olacak şekilde güncellerseniz VStack, yine de aynı genişlikte olduğunu fark edeceksiniz .

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)
        }
    }
}

Bunun nedeni .frame(...), görünüm hiyerarşisine aslında başka bir görünüm eklemesi ve bu görünümün ekranı doldurmasıdır. Ancak VStackyine de yok.

Bu sorun benim cevabımda da aynı görünüyor ve yukarıdaki ile aynı yaklaşım kullanılarak kontrol edilebilir (öncesinde ve sonrasında farklı arka plan renkleri koymak .frame(...). Aslında bunu genişletmenin tek yolu VStackboşluk kullanmaktır:

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)
    }
}

Denedim relativeWidth, işe yaramadı. Daha önce yerleştirilirse bir şekilde GeometryReaderçalışır.frame.background(Color.red)
ielyamani

GeometryReaderB'den sonra "Değiştirici Eklediğimde" Show SwiftUI Inspectorveya kullanıcı arayüzünün başka herhangi bir yerinde gösterilen listede göremiyorum . Nasıl keşfettiniz GeometryReader?
gitti

1
@gone - Stack Overflow :)
Jake

17

Swift 5.2 ve iOS 13.4 ile ihtiyaçlarınıza göre aşağıdaki örneklerden birini VStacken önde gelen kısıtlamalarla ve tam boyutlu bir çerçeveyle uyumlu hale getirmek için kullanabilirsiniz .

Aşağıdaki kod parçacıklarının hepsinin aynı görüntü ile sonuçlandığını, ancak görünüm hiyerarşisinde hata ayıklama sırasında görünebilecek öğelerin etkili çerçevesini VStackveya sayısını garanti etmediğini unutmayın View.


1. frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)Yöntemi kullanma

En basit yaklaşım, çerçevenizi VStackmaksimum genişlik ve yüksekliğe ayarlamak ve ayrıca gerekli hizalamayı şuradan geçirmektir 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)
    }

}

Alternatif olarak, e-postalarınız için belirli hizalamayla maksimum çerçeve belirlemek Viewkod tabanınızda yaygın bir modelse, bunun için bir genişletme yöntemi oluşturabilirsiniz 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. SpacerHizalamayı zorlamak için s kullanma

İçinizi VStacktam boyutta gömebilir HStackve üstteki hizalamanızı Spacerzorlamak için VStackson ve alt s kullanabilirsiniz :

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. a ZStackve tam boyutlu bir arka plan kullanmaView

Bu örnek, üstte bir satır aralığı hizalamasına sahip bir a'nın VStackiçine nasıl gömüleceğinizi gösterir ZStack. ColorMaksimum genişliği ve yüksekliği ayarlamak için görünümün nasıl kullanıldığına dikkat edin:

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. kullanma GeometryReader

GeometryReaderaşağıdaki beyana sahiptir :

İçeriğini kendi boyutunun ve koordinat alanının bir işlevi olarak tanımlayan bir kap görünümü. [...] Bu görünüm, üst düzenine esnek bir tercih edilen boyut döndürür.

Aşağıdaki kod parçacığı , en önde gelen kısıtlamalara ve tam boyutlu bir çerçeveye GeometryReaderuygun hale getirmek için nasıl kullanılacağını gösterir VStack:

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:)yöntemi kullanma

VStackMevcut bir tam boyutun üzerine en önde gelen kısıtlamalarla hizalamak Viewistiyorsanız, overlay(_:alignment:)yöntemi kullanabilirsiniz :

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
            )
    }

}

Görüntüle:


9

Sorunu çözmenin en basit yolu bir ZStack + .edgesIgnoringSafeArea (.all) kullanmaktı.

struct TestView : View {

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

4

Kullanarak yapabilirsiniz GeometryReader

Geometri Okuyucu

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)
            }
        }
    }
}

Çıktınız şöyle:

görüntü açıklamasını buraya girin



4

İyi bir çözüm ve "mekanizmalar" olmadan unutulmuş olan ZStack

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

Sonuç:

görüntü açıklamasını buraya girin


3

Bir başka alternatif de, alt görüntülerden birini bir öğesinin içine yerleştirmek ve onun arkasına bir HStackyerleştirmektir Spacer():

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)
    }
}

sonuçlanan :

Bir VStack içinde HStack


1
Imho, bu en zarif çözüm
Vyacheslav

3

Benim için işe yarayan şey buydu ( ScrollView(isteğe bağlı), böylece gerekirse daha fazla içerik ve ortalanmış içerik eklenebilir):

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

Sonuç

merkezli


2

Bunun herkes için işe yaramayacağını biliyorum, ancak sadece bir Bölücü eklemenin bunu çözmesinin ilginç olduğunu düşündüm.

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

2

Bu yararlı bir kod parçasıdır:

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

Sonuçları .expandable()değiştiricili ve değiştiricisiz karşılaştırın:

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

-

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

görüntü açıklamasını buraya girin


2

Ebeveyni doldurmak için GeometryReader'ı kullanışlı bir uzantıda kullanabilirsiniz.

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)
        }
    }
}

bu nedenle istenen örneği kullanarak

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)
    }
}

(ara parçaya artık gerek olmadığını unutmayın)

görüntü açıklamasını buraya girin


2

görüntü açıklamasını buraya girin

Kullanarak Giriş Sayfası tasarımı 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()
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.