VStack Alignment Issue

Hi Apple developer,

I'm totally new in the development world using SwiftUI with just a little experience of Flutter.

Currently I'm struggling with the combination of VStack and the alignment of the sub views. I'm using a LazyVGrid with multiple Rectangles and overlays inside. The overlay contains a VStack with a leading alignment. And here is the problem. I framed them with a border to visualize the limits of these views. But it seams, that the leading alignment is not working properly. There is a large gap on the left side of the Image() and Text() views and I don't know why.

I'm very happy for any advice.

Here is my code of this view.

Thanks a lot!

import SwiftUI
import SwiftData

extension UIScreen {
    static let screenWidth: CGFloat = UIScreen.main.bounds.size.width
    static let screenHeight: CGFloat = UIScreen.main.bounds.size.height
    static let screenSize: CGSize = UIScreen.main.bounds.size
}

struct TestView: View {
    let constants: Constants = Constants()
    let columnCount: Int = 2
    let gridSpacing: CGFloat = 10
    let gridRadius: CGFloat
    let gridWidth: CGFloat
    let gridHeight: CGFloat
    
    let gridItems = [
        Item(title: "Total", color: Color.gray, image: "dollarsign.circle.fill")
    ]
    
    @State private var isSheetPresented: Bool = false
    
    init() {
        gridWidth = UIScreen._screenWidth / 2 - 3 * gridSpacing
        gridHeight = 1.25 * gridWidth
        gridRadius = gridWidth / 7.5
    }
    
    var body: some View {
        NavigationStack {
            ScrollView(.vertical) {
                LazyVGrid(columns: Array(
                    repeating: .init(.flexible(), spacing: -2 * gridSpacing),
                    count: columnCount),
                    spacing: 2 * gridSpacing) {
                        ForEach(gridItems, id: \.title) { item in
                            RoundedRectangle(cornerRadius: gridRadius, style: .continuous)
                                .fill(item.color.gradient)
                                .frame(width: gridWidth, height: gridHeight)
                                .overlay(
                                    VStack(alignment: .leading) {
                                        Image(systemName: item.image)
                                            .colorInvert()
                                            .font(.system(size: 30))
                                            .border(Color.yellow)
                                        Spacer()
                                        Text(item.title)
                                            .font(.system(size: 20))
                                            .foregroundColor(.white)
                                            .border(Color.yellow)
                                    }
                                    .padding([.top, .bottom])
                                    .frame(maxWidth: .infinity)
                                    .border(Color.black)
                                )
                        }
                }.padding(.top)
            }
            .navigationTitle("Test View")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Menu {
                        Button("Add", action: {})
                    } label: {
                        Image(systemName: "ellipsis.circle")
                    }
                }
            }
        }
    }
}

struct Item: Identifiable {
    let id: UUID = UUID()
    let title: String
    let color: Color
    let image: String
}

#Preview {
    TestView()
}
Answered by darkpaw in 821006022

I think you're misunderstanding what the alignment parameter does in a VStack.

It aligns the subviews in the stack, not the stack itself.

Here's a visual example:

import SwiftUI

struct ContentView: View {
    var body: some View {
		VStack(alignment: .leading) {
			Image(systemName: "arrow.2.circlepath")
				.colorInvert()
				.font(.system(size: 30))
				.border(Color.yellow)

			Text("alignment: .leading")
				.font(.system(size: 20))
				.foregroundStyle(Color.white)
				.border(Color.yellow)
		}
		.frame(maxWidth: .infinity)
		.background(Color.black)

		VStack(alignment: .trailing) {
			Image(systemName: "arrow.2.circlepath")
				.colorInvert()
				.font(.system(size: 30))
				.border(Color.yellow)

			Text("alignment: .trailing")
				.font(.system(size: 20))
				.foregroundStyle(Color.white)
				.border(Color.yellow)
		}
		.frame(maxWidth: .infinity)
		.background(Color.green)

		HStack {
			VStack(alignment: .leading) {
				Image(systemName: "arrow.2.circlepath")
					.colorInvert()
					.font(.system(size: 30))
					.border(Color.yellow)

				Text("alignment: .leading in an HStack")
					.font(.system(size: 20))
					.foregroundStyle(Color.white)
					.border(Color.yellow)
			}
			Spacer()  // <<-- Remove this line and see the difference
		}
		.frame(maxWidth: .infinity)
		.background(Color.blue)
    }
}


#Preview {
    ContentView()
}

In the blue section, where I've said to remove the line with the Spacer() this is to show you the effect of using an HStack, which is what I think you want.

The HStack simply lines up views horizontally, and when you add in a Spacer() it pushes the content along, so this pushes the text to the left:

|SOME TEXT HERE<---Spacer()--->|

This pushes the text to the right:

|<---Spacer()--->SOME TEXT HERE|

and this spreads it out:

|SOME<---Spacer()--->TEXT<---Spacer()--->HERE|

Accepted Answer

I think you're misunderstanding what the alignment parameter does in a VStack.

It aligns the subviews in the stack, not the stack itself.

Here's a visual example:

import SwiftUI

struct ContentView: View {
    var body: some View {
		VStack(alignment: .leading) {
			Image(systemName: "arrow.2.circlepath")
				.colorInvert()
				.font(.system(size: 30))
				.border(Color.yellow)

			Text("alignment: .leading")
				.font(.system(size: 20))
				.foregroundStyle(Color.white)
				.border(Color.yellow)
		}
		.frame(maxWidth: .infinity)
		.background(Color.black)

		VStack(alignment: .trailing) {
			Image(systemName: "arrow.2.circlepath")
				.colorInvert()
				.font(.system(size: 30))
				.border(Color.yellow)

			Text("alignment: .trailing")
				.font(.system(size: 20))
				.foregroundStyle(Color.white)
				.border(Color.yellow)
		}
		.frame(maxWidth: .infinity)
		.background(Color.green)

		HStack {
			VStack(alignment: .leading) {
				Image(systemName: "arrow.2.circlepath")
					.colorInvert()
					.font(.system(size: 30))
					.border(Color.yellow)

				Text("alignment: .leading in an HStack")
					.font(.system(size: 20))
					.foregroundStyle(Color.white)
					.border(Color.yellow)
			}
			Spacer()  // <<-- Remove this line and see the difference
		}
		.frame(maxWidth: .infinity)
		.background(Color.blue)
    }
}


#Preview {
    ContentView()
}

In the blue section, where I've said to remove the line with the Spacer() this is to show you the effect of using an HStack, which is what I think you want.

The HStack simply lines up views horizontally, and when you add in a Spacer() it pushes the content along, so this pushes the text to the left:

|SOME TEXT HERE<---Spacer()--->|

This pushes the text to the right:

|<---Spacer()--->SOME TEXT HERE|

and this spreads it out:

|SOME<---Spacer()--->TEXT<---Spacer()--->HERE|

VStack Alignment Issue
 
 
Q