Search code examples
swiftuiswiftui-vstack

How to align a VStack with 80% width with Texts centrally when it is in a ZStack?


I want to place a VStack including a few Text-Views in the center of the screen with a white background and opacity 50%. Therefore I tried the GeometryReader and know that it tries to consume 100% of the available size, but the end result looks strange. When I try to inspect the elements it seems to be caused by the ZStack containing the background image.

What do I need to do to align the VStack in the center?

ZStack {
            Image("demo-bg")
                .resizable()
                .scaledToFill()
                .edgesIgnoringSafeArea(.all)
            GeometryReader { proxy in
                VStack {

                    Text("Test1")
                        .font(.largeTitle)
                        .foregroundStyle(.green)
                    Text("Test2")
                        .font(.largeTitle)
                        .foregroundStyle(.red)
                    Text("Test3")
                        .font(.largeTitle)
                        .foregroundStyle(.green)
                    Text("Test4")
                        .font(.largeTitle)
                    TextField("Result", text: $result).keyboardType(.numberPad)
                }
                .frame(width: proxy.size.width * 0.8)
                .background(.white)
            }
        }

/Preview in XCode


Solution

  • I would suggest showing the Image using a .background modifier, so that the size of the image doesn't affect the layout. Then:

    • The ZStack is not needed any more, you can apply the image as background to the GeometryReader instead.
    • As you mentioned, a GeometryReader grabs all the available space. Unlike HStack, VStack and ZStack, which use center-alignment by default, the content of a GeometryReader is positioned with top-leading alignment. So after you have sized the main content to the size you want it to have and applied a background color (white), increase its size using maxWidth: .infinity, maxHeight: .infinity. This way, it fills the space of the GeometryReader.
    • The .frame modifier uses center alignment by default. If in fact you want some other kind of alignment, add an alignment parameter too.
    • Also, .edgesIgnoringSafeArea is deprecated, suggest using .ignoresSafeArea instead.
    var body: some View {
        GeometryReader { proxy in
            VStack {
                // ...
            }
            .frame(width: proxy.size.width * 0.8)
            .background(.white)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
        .background {
            Image("demo-bg")
                .resizable()
                .scaledToFill()
                .ignoresSafeArea()
        }
    }
    

    Screenshot