SwiftUIでVStackを画面の幅いっぱいに表示する


119

このコードを考えると:

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

それはこの相互作用をもたらします:

プレビュー

VStackラベル/テキストコンポーネントが全幅を必要としない場合でも、どうすれば画面の幅を塗りつぶすことができますか?

私が見つけたトリックは、次のように構造体に を挿入するHStackことです。

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

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

    Spacer()
}

これにより、目的のデザインが得られます。

必要な出力

もっと良い方法はありますか?


1
同様の結果を得るための最大5つの代替方法を示す私の回答を参照しください。
今のプチ

回答:


193

次のオプションを指定して.frame修飾子を使用してみてください。

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

これは柔軟なフレームであると説明されており(ドキュメントを参照)、画面全体に広がるように伸び、余分なスペースがあると、コンテンツが中央に配置されます。


サブビューは、(左に整列しません.leading)。前に置かれた場合、完全な幅を取る.background(Color.red)
ielyamani

左揃えのコードも追加しました。今正確に一致する必要があります
svarrall

12
これは間違っています。.backgound(Color.red)フレームのビューの背景ではなく、背景のあるVStackフレームビューです。(この理由はWWDCビデオで説明されています:P)。.background(Color.green)前に置くと、フレームの背景が赤であるのに.frameVStackまだコンテンツに合う幅があることがわかります...置くと.frame(...)、編集ではなくVStack、実際に別のビューが作成されます。
ジェイク

2
@Jakeは、災害のためのレシピのように思える
Sudara

1
質問があります:VStack(alignment:.leading)があるので、なぜalignment:.topLeadingを使用するのですか?? 共有するためのおかげで
UIChris

36

動作し、おそらくもう少し直感的な代替のスタッキング配置は次のとおりです。

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

Spacer()必要に応じて'を削除することで、コンテンツを簡単に再配置することもできます。

位置を変更するためのスペーサーの追加/削除


4
良い代替は、GIF👍🏻Liked
ielyamani

1
これは、より直感的であり、OPの質問と一致していることがわかりました。この質問は、要素の周囲にコンテナーを挿入する方法ではなく、Stack要素をコンテナーに充填する方法についてでした。
brotskydotcom

1
私は、これはSwiftUI背後にある考え方と整合される最良の答えであることがわかりました
krummens

1
このような一般的なレイアウトについて、ここで最もSwiftUI-yの答えは間違いありません。
dead_can_dance

30

より良い方法があります!

VStack塗りつぶしをその親の幅にするために、を使用しGeometryReaderてフレームを設定できます。(動作する.relativeWidth(1.0)はずですが、明らかに今は動作しません)

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

VStack実際の画面の幅を作成するには、UIScreen.main.bounds.widthを使用する代わりにフレームを設定するときに使用できますGeometryReaderが、親ビューの幅が必要だったと思います。

また、この方法は、あなたの中にスペースを追加しないというメリットがあるVStack(あなたが間隔た場合)あなたが追加した場合、どの起こるかもしれないHStackとのSpacer()に対して、それの内容としてはVStack

更新-より良い方法はありません!

受け入れられた答えをチェックした後、私は受け入れられた答えが実際には機能しないことに気づきました!一見機能しているように見えますが、を更新VStackして背景を緑色にVStackすると、幅が同じであることがわかります。

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

これは、.frame(...)実際にはビュー階層に別のビューを追加していて、そのビューが画面全体に表示されるためです。しかし、VStackそれでもそうではありません。

この問題は私の回答でも同じようで、上記と同じアプローチを使用して確認できます(前後に異なる背景色を配置し.frame(...)ます。実際に広がるように見える唯一の方法VStackは、スペーサーを使用することです。

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

試したがrelativeWidth、うまくいかなかった。どうにかGeometryReaderならば作品.frameの前に置かれている.background(Color.red)
ielyamani

GeometryReader後に「修飾子を追加」したときに表示されるリストShow SwiftUI Inspector、またはUI内の他の場所に表示されません。どのようにして発見しましたGeometryReaderか?
消え

1
@ gone-Stack Overflow :)
Jake

19

Swift5.2とiOS13.4では、必要に応じて、次の例のいずれかを使用してVStack、主要な制約とフルサイズのフレームに合わせることができます。

以下のコードスニペットはすべて同じ表示になりますが、ビュー階層のデバッグ中に表示される可能性のVStackあるView要素の有効なフレームや数を保証するものではないことに注意してください。


1.frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:)方法を使用する

最も簡単なアプローチはVStack、最大の幅と高さでフレームを設定し、必要な配置を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)
    }

}

別の方法として、Viewsに特定の配置で最大フレームを設定することがコードベースの一般的なパターンである場合は、その上に拡張メソッドを作成できます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.sを使用してSpacer位置合わせを強制します

VStack内側をフルサイズで埋め込み、HStackトレーリングとボトムを使用してSpacerVStackトップリーディングアライメントを強制することができます。

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.ZStackとフルサイズの背景を使用するView

この例は、トップリーディングアライメントを持つVStack内部に埋め込む方法を示していますZStackColorビューを使用して最大の幅と高さを設定する方法に注意してください。

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.使用する GeometryReader

GeometryReader次の宣言があります:

コンテンツを独自のサイズと座標空間の関数として定義するコンテナビュー。[...]このビューは、親レイアウトに柔軟な優先サイズを返します。

以下のコードスニペットは、トップリーディング制約とフルサイズフレームGeometryReaderに合わせるために使用する方法を示しています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:)メソッドの使用

VStack既存のフルサイズの上に最上位の制約を揃えたい場合はView、次のoverlay(_:alignment:)方法を使用できます。

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

}

表示:


10

この問題を解決する最も簡単な方法は、ZStack + .edgesIgnoringSafeArea(.all)を使用することでした。

struct TestView : View {

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


4

あなたはを使用してそれを行うことができます GeometryReader

GeometryReader

コード:

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

次のような出力:

ここに画像の説明を入力してください


3

もう1つの方法は、サブビューの1つをの中にHStack配置し、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)
    }
}

その結果 :

VStack内のHStack


1
Imho、これは最もエレガントなソリューションです
Vyacheslav

3

これは私にとってうまくいったことです(ScrollView(オプション)ので、必要に応じてより多くのコンテンツを追加でき、さらに中央に配置されたコンテンツ):

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

結果

中央揃え


3

「制約」のない良い解決策は忘れられています ZStack

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

結果:

ここに画像の説明を入力してください


2

これがすべての人に役立つわけではないことはわかっていますが、Dividerを追加するだけでこれが解決するのは興味深いと思いました。

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

2

これは便利なコードです。

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

.expandable()修飾子がある場合とない場合の結果を比較します。

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

-

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

ここに画像の説明を入力してください


2

ここに画像の説明を入力してください

ログインページのデザインを使用して 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()
    }
}

1

便利な拡張機能でGeometryReaderを使用して、親を埋めることができます

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

したがって、要求された例を使用すると、

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

(スペーサーは不要になりました)

ここに画像の説明を入力してください


これはフルスクリーンではありません。
アヒル

質問は全画面表示ではなく、全幅を要求しました。安全領域を超えて拡張する場合は、を使用します。EdgesIgnoringSafeArea
混乱したVorlon20年

0
 var body: some View {
      VStack {
           CarouselView().edgesIgnoringSafeArea(.all)           
           List {
                ForEach(viewModel.parents) { k in
                    VideosRowView(parent: k)
                }
           }
    }
 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.