swiftswiftuivstackhstack

HStack Center alignment while keeping a VStack leading alignment


I am wanting to display three items across a screen equally. I am trying to make them all centered while having a leading alignment so when there is less than three they still maintain the same look. Im not very familiar with Lazy stack so I'm sure this is where my issue is coming into play. Currently if I have over three items the layout is correct, anything less than that the items do not have a leading alignment.

ScrollView {
    LazyHStack(alignment: .center) {
        LazyVStack(alignment: .leading, spacing: 10) {
            ForEach(books.chunked(into: 3), id: \.self) { chunk in
                LazyHStack(spacing: 30) {
                    ForEach(chunk, id: \.self) { book in
                        NavigationLink {
                            QuoteList(book: book)
                        } label: {
                            BookView(book: book)
                                .tint(.black)
                        }
                        .contextMenu {
                            Button("Delete") {
                                if let index = books.firstIndex(where: { $0.id == book.id}) {
                                    modelContext.delete(books[index])
                                    deleteBookTipDone.invalidate(reason: .actionPerformed)
                                }
                            }
                        }
                    }
                }
            }
            .padding(.horizontal)
        }
        .padding(.bottom)
    }
}

Solution

  • You can simply use .frame(maxWidth: .infinity) on your LazyVStack.
    But here is another solution you might want to implement, using LazyGrids

    First, I created some mock data just to show how it works, but you will use your own real data.

    let books = [1,2,3,4,5] // This will be your array of Books
    let columns = Array(repeating: GridItem(.flexible(), spacing: 15, alignment: .leading), count: 3)
    

    Notice that I created the columns for the grid using a repeating array.

    Next, I created a BookView. In this case just a rectangle with some text.

    struct BookView: View {
        var body: some View {
            VStack(alignment: .leading, spacing: 0) {
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color.brown)
                    .frame(height: 150)
                
                Text("Book name")
                    .font(.body)
                    .fontWeight(.medium)
                    .foregroundStyle(Color.black)
                    .padding(.top, 10)
                
                Text("Author")
                    .font(.subheadline)
                    .foregroundStyle(Color.gray)
            }
            .frame(maxWidth: .infinity)
        }
    }
    

    Lastly, the grid itself. We use forEach to loop through all the books show them as a NavigationLink so you link to the QuoteList screen. I also added some horizontal padding just for looks...

    LazyVGrid(columns: columns, spacing: 15) {
        ForEach(books, id: \.self) { book in
            NavigationLink {
                // QuoteList(book: book)
            } label: {
                BookView()
            }
            .contextMenu {
                Button("Delete") {
                    // Code
                }
            }
        }
    }
    .padding(.horizontal, 15)
    

    Now, if you want to add your top section you can do something like the following. This is the entire code all put together:

    struct ContentView: View {
        var body: some View {
            
            let books = [1,2,3,4,5] // This will be your array of Books
            let columns = Array(repeating: GridItem(.flexible(), spacing: 15, alignment: .leading), count: 3)
            
            NavigationStack {
                ScrollView {
                    VStack(spacing: 30) {
                        
                        // This is the top section
                        ScrollView(.horizontal) {
                            HStack(spacing: 20) {
                                ForEach(0..<5) { _ in
                                    RoundedRectangle(cornerRadius: 12)
                                        .fill(Color.gray)
                                        .frame(width: 200, height: 150)
                                }
                            }
                            .padding(.leading, 15)
                        }
                        .padding(.top, 30)
                        
                        // This is the Books section
                        LazyVGrid(columns: columns, spacing: 15) {
                            ForEach(books, id: \.self) { book in
                                NavigationLink {
                                    // QuoteList(book: book)
                                } label: {
                                    BookView()
                                }
                                .contextMenu {
                                    Button("Delete") {
                                        // Code
                                    }
                                }
                            }
                        }
                        .padding(.horizontal, 15)
                    }
                    .navigationTitle("Library")
                }
            }
        }
    }
    
    struct BookView: View {
        var body: some View {
            VStack(alignment: .leading, spacing: 0) {
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color.brown)
                    .frame(height: 150)
                
                Text("Book name")
                    .font(.body)
                    .fontWeight(.medium)
                    .foregroundStyle(Color.black)
                    .padding(.top, 10)
                
                Text("Author")
                    .font(.subheadline)
                    .foregroundStyle(Color.gray)
            }
            .frame(maxWidth: .infinity)
        }
    }
    

    Preview

    enter image description here enter image description here