swiftswiftui

Unable to align 2 column view to leading edge in Swift UI


This is probably a basic question but I’m really struggling reliably aligning simple groups of items in Swift UI and have not found a simple solution.

I have a number of screens in an app I’m working on which combine a text string and a button but I am struggling to align these without using many VStacks within VStacks. My understanding of Swift UI was that this should not be necessary for simple visual structures - and that overcomplicating the visual layout could potentially cause issues further down the lines.

I have used the lazyGrid view with a 2 column structure, wrapped in a VStack which is aligned. I have tried a few combinations of aligning the elements to the leading edge but I am still seeing them apparently aligning to the centre.

Could someone please advise.

import SwiftUI

struct ContentView: View {
    
    let column = [
        GridItem(.fixed(200))
    ]
    
    let columns = [
        GridItem(.fixed(200)),
        GridItem(.flexible(minimum: 50, maximum: .infinity))
    ]
        
    var body: some View {
        
        VStack(alignment: .leading) {
            
            //  ################################################################################
            //  Lazy Grid - no HStack
            //  ################################################################################
                
                VStack {

                    Text("Secondary Items").fontWeight(.bold)

                    LazyVGrid(columns: columns, spacing: 20) {
                        
                        Text("Clear Files")
                        
                        Button(action: {
                        }) {
                            HStack(spacing: 10) {
                                Text("Clear")
                            }
                        }
                    }
                    .background(Color.green.opacity(0.0))
                  //  .border(Color.red)
                    
                    LazyVGrid(columns: columns, spacing: 20) {
                        
                        Text("Clear Scripts")
                        
                        Button(action: {
                            
                        }) {
                            HStack(spacing: 10) {
                                Text("Clear")
                            }
                        }
                    }
                    .background(Color.green.opacity(0.0))
                  //  .border(Color.red)
                    
                    LazyVGrid(columns: columns, spacing: 20) {
                        
                        Text("Delete General")
                        
                        Button(action: {
                            print("Check processingComplete")
                        }) {
                            HStack(spacing: 10) {
                                Text("Delete")
                            }
                        }
                    }
                    .background(Color.green.opacity(0.0))
                  //  .border(Color.red)
                    
                    LazyVGrid(columns: columns, spacing: 20) {
                        
                        Text("Clear Root")
                        
                        Button(action: {
                        }) {
                            HStack(spacing: 10) {
                                Text("Clear")
                            }
                        }
                    }
                    .background(Color.green.opacity(0.0))
                  //  .border(Color.red)
                    
                    LazyVGrid(columns: columns, spacing: 20) {
                        
                        Text("Clear Encryption")
                        
                        Button(action: {
                            
                        }) {
                            HStack(spacing: 10) {
                                Text("Clear")
                            }
                        }
                    }
                    .background(Color.green.opacity(0.0))
                  //  .border(Color.red)
                }
                .frame(width: 400, alignment: .leading)
                .padding()
            }
            .padding()
            Spacer()
    }
}

enter image description here


Solution

  • You are indeed overcomplicating this. LazyVGrid is a "grid". You only need one of these to display a grid of views. You do not need one LazyVGrid for each row.

    You should put all the Text-Button pairs into one LazyVGrid. The alignment of each column can be specified as part of the GridItem.

    let columns = [
        GridItem(.fixed(200), alignment: .leading), // <------ HERE!
        GridItem(.flexible(minimum: 50, maximum: .infinity))
    ]
    
    var body: some View {
        VStack {
            Text("Secondary Items").fontWeight(.bold)
            LazyVGrid(columns: columns, spacing: 20) {
                Text("Clear Files")
                Button("Clear") {}
                
                Text("Clear Scripts")
                Button("Clear") {}
                
                Text("Delete General")
                Button("Delete") {}
                
                Text("Clear Root")
                Button("Clear") {}
                
                Text("Clear Encryption")
                Button("Clear") {}
            }
        }
        .frame(width: 400, alignment: .leading)
        .padding()
    }
    

    Since the grid is not scrollable, LazyVGrid isn't actually lazy. I'd suggest using a regular Grid instead.

    var body: some View {
        
        Grid(horizontalSpacing: 160) {
            GridRow {
                Text("Secondary Items").fontWeight(.bold)
                    .gridCellColumns(2)
            }
            GridRow {
                Text("Clear Files")
                    .gridCellAnchor(.leading)
                Button("Clear") {}
            }
            
            GridRow {
                Text("Clear Scripts")
                    .gridCellAnchor(.leading)
                Button("Clear") {}
            }
            
            GridRow {
                Text("Delete General")
                    .gridCellAnchor(.leading)
                Button("Delete") {}
            }
            
            GridRow {
                Text("Clear Root")
                    .gridCellAnchor(.leading)
                Button("Clear") {}
            }
            
            GridRow {
                Text("Clear Encryption")
                    .gridCellAnchor(.leading)
                Button("Clear") {}
            }
        }
        .padding()
    }
    

    The alignment of the views can be set using gridCellAnchor. If you want the buttons to be leading-aligned as well, you can pass the alignment to Grid.init:

    Grid(alignment: .leading, horizontalSpacing: 160) {
    //   ^^^^^^^^^^^^^^^^^^^
        GridRow {
            Text("Secondary Items").fontWeight(.bold)
                .gridCellColumns(2)
                .gridCellAnchor(.center) // now this needs to be centered
        }
        GridRow {
            Text("Clear Files") // don't need .gridCellAnchor here
            Button("Clear") {}
        }
        // and so on...
    }