[ios] Make a VStack fill the width of the screen in SwiftUI

Given this code :

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

It results in this interace:

preview

How can I make the VStack fill the width of the screen even if the labels/text components don't need the full width?

A trick I've found is to insert an empty HStack in the structure like so:

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

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

    Spacer()
}

Which yields the desired design:

desired output

Is there a better way?

This question is related to ios swift xcode swiftui

The answer is


The simplest way I manage to solve the issue was is by using a ZStack + .edgesIgnoringSafeArea(.all)

struct TestView : View {

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

A good solution and without "contraptions" is the forgotten ZStack

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

Result:

enter image description here


This is what worked for me (ScrollView (optional) so more content can be added if needed, plus centered content):

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

Result

centered


An alternative stacking arrangement which works and is perhaps a bit more intuitive is the following:

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

The content can also easily be re-positioned by removing the Spacer()'s if necessary.

adding/removing Spacers to change position


One more alternative is to place one of the subviews inside of an HStack and place a Spacer() after it:

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

resulting in :

HStack inside a VStack


This is a useful bit of code:

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

Compare the results with and without the .expandable() modifier:

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

-

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

enter image description here


 var body: some View {
      VStack {
           CarouselView().edgesIgnoringSafeArea(.all)           
           List {
                ForEach(viewModel.parents) { k in
                    VideosRowView(parent: k)
                }
           }
    }
 }

You can use GeometryReader in a handy extension to fill the parent

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

so using the requested example, you get

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

(note the spacer is no longer needed)

enter image description here


There is a better way!

To make the VStack fill the width of it's parent you can use a GeometryReader and set the frame. (.relativeWidth(1.0) should work but apparently doesn't right now)

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

To make the VStack the width of the actual screen you can use UIScreen.main.bounds.width when setting the frame instead of using a GeometryReader, but I imagine you likely wanted the width of the parent view.

Also, this way has the added benefit of not adding spacing in your VStack which might happen (if you have spacing) if you added an HStack with a Spacer() as it's content to the VStack.

UPDATE - THERE IS NOT A BETTER WAY!

After checking out the accepted answer, I realized that the accepted answer doesn't actually work! It appears to work at first glance, but if you update the VStack to have a green background you'll notice the VStack is still the same width.

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

This is because .frame(...) is actually adding another view to the view hierarchy and that view ends up filling the screen. However, the VStack still does not.

This issue also seems to be the same in my answer as well and can be checked using the same approach as above (putting different background colors before and after the .frame(...). The only way that appears to actually widen the VStack is to use spacers:

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

enter image description here

Login Page design using 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()
    }
}

I know this will not work for everyone, but I thought it interesting that just adding a Divider solves for this.

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

With Swift 5.2 and iOS 13.4, according to your needs, you can use one of the following examples to align your VStack with top leading constraints and a full size frame.

Note that the code snippets below all result in the same display, but do not guarantee the effective frame of the VStack nor the number of View elements that might appear while debugging the view hierarchy.


1. Using frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:) method

The simplest approach is to set the frame of your VStack with maximum width and height and also pass the required alignment 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)
    }

}

As an alternative, if setting maximum frame with specific alignment for your Views is a common pattern in your code base, you can create an extension method on View for it:

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. Using Spacers to force alignment

You can embed your VStack inside a full size HStack and use trailing and bottom Spacers to force your VStack top leading alignment:

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. Using a ZStack and a full size background View

This example shows how to embed your VStack inside a ZStack that has a top leading alignment. Note how the Color view is used to set maximum width and height:

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

GeometryReader has the following declaration:

A container view that defines its content as a function of its own size and coordinate space. [...] This view returns a flexible preferred size to its parent layout.

The code snippet below shows how to use GeometryReader to align your VStack with top leading constraints and a full size frame:

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. Using overlay(_:alignment:) method

If you want to align your VStack with top leading constraints on top of an existing full size View, you can use overlay(_:alignment:) method:

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

}

Display:


You can do it by using 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)
            }
        }
    }
}

Your output like:

enter image description here


use this

.edgesIgnoringSafeArea(.all)

Examples related to ios

Adding a UISegmentedControl to UITableView Crop image to specified size and picture location Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Autoresize View When SubViews are Added Warp \ bend effect on a UIView? Speech input for visually impaired users without the need to tap the screen make UITableViewCell selectable only while editing Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64

Examples related to swift

Make a VStack fill the width of the screen in SwiftUI Xcode 10.2.1 Command PhaseScriptExecution failed with a nonzero exit code Command CompileSwift failed with a nonzero exit code in Xcode 10 Convert Json string to Json object in Swift 4 iOS Swift - Get the Current Local Time and Date Timestamp Xcode 9 Swift Language Version (SWIFT_VERSION) How do I use Safe Area Layout programmatically? How can I use String substring in Swift 4? 'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator Safe Area of Xcode 9 The use of Swift 3 @objc inference in Swift 4 mode is deprecated?

Examples related to xcode

Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64 iPhone is not available. Please reconnect the device Make a VStack fill the width of the screen in SwiftUI error Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65 The iOS Simulator deployment targets is set to 7.0, but the range of supported deployment target version for this platform is 8.0 to 12.1 Xcode 10.2.1 Command PhaseScriptExecution failed with a nonzero exit code Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) Xcode 10: A valid provisioning profile for this executable was not found Xcode 10, Command CodeSign failed with a nonzero exit code

Examples related to swiftui

Make a VStack fill the width of the screen in SwiftUI SwiftUI - How do I change the background color of a View? Add bottom line to view in SwiftUI / Swift / Objective-C / Xamarin How can I programmatically check whether a keyboard is present in iOS app?