Skip to main content

How to position views in a fixed grid

About 3 minSwiftSwiftUIArticle(s)bloghackingwithswift.comcrashcourseswiftswiftuixcodeappstore

How to position views in a fixed grid 관련

SwiftUI by Example

Back to Home

How to position views in a fixed grid | SwiftUI by Example

How to position views in a fixed grid

Updated for Xcode 15

New in iOS 16

SwiftUI's Grid view lets us create a static grid of views, with precise control over what goes into each row and column. You mark out individual rows using GridRow, then optionally also configure how wide each cell should be.

As a basic example, this creates a 2x2 grid with text reflecting where each cell with be positioned:

Grid {
    GridRow {
        Text("Top Leading")
            .background(.red)

        Text("Top Trailing")
            .background(.orange)
    }

    GridRow {
        Text("Bottom Leading")
            .background(.green)

        Text("Bottom Trailing")
            .background(.blue)
    }
}
.font(.title)

Download this as an Xcode projectopen in new window

If you don't want to have the same number of cells in each row, you have three choices.

First, if you do nothing, SwiftUI will automatically insert empty cells to make sure the rows are equal. So, in this code we can add to the red and blue scores freely, and SwiftUI will keep the whole thing balanced:

struct ContentView: View {
    @State private var redScore = 0
    @State private var blueScore = 0

    var body: some View {
        Grid {
            GridRow {
                Text("Red")

                ForEach(0..<redScore, id: \.self) { _ in
                    Rectangle()
                        .fill(.red)
                        .frame(width: 20, height: 20)
                }
            }

            GridRow {
                Text("Blue")

                ForEach(0..<blueScore, id: \.self) { _ in
                    Rectangle()
                        .fill(.blue)
                        .frame(width: 20, height: 20)
                }
            }
        }
        .font(.title)

        Button("Add to Red") { redScore += 1 }
        Button("Add to Blue") { blueScore += 1 }
    }
}

Download this as an Xcode projectopen in new window

The second option is to place views into the grid without wrapping them in a GridRow, which will cause them to occupy a whole row by themselves. This is great for the Divider view. The third option is to use the gridCellColumns() modifier, to make one cell span multiple columns.

We can see the second and third option in a single code sample:

Grid {
    GridRow {
        Text("Food")
        Text("$200")
    }

    GridRow {
        Text("Rent")
        Text("$800")
    }

    GridRow {
        Text("Candles")
        Text("$3600")
    }

    Divider()

    GridRow {
        Text("$4600")
            .gridCellColumns(2)
            .multilineTextAlignment(.trailing)
    }

}
.font(.title)

Download this as an Xcode projectopen in new window

As you can see, using gridCellColumns() with the same number of columns you have yields the same result as placing a view outside of a GridRow.

Important: Unlike LazyHGrid and LazyVGrid, a plain Grid loads all its views immediately, so be careful how much work you do.

Grids are fantastic choices when you need exact layouts – we can use them to make a tic-tac-toe board:

Grid(horizontalSpacing: 20, verticalSpacing: 20) {
    GridRow {
        Image(systemName: "xmark")
        Image(systemName: "xmark")
        Image(systemName: "xmark")
    }

    GridRow {
        Image(systemName: "circle")
        Image(systemName: "xmark")
        Image(systemName: "circle")
    }

    GridRow {
        Image(systemName: "xmark")
        Image(systemName: "circle")
        Image(systemName: "circle")
    }
}
.font(.largeTitle)

Download this as an Xcode projectopen in new window

Or even a chessboard:

struct ContentView: View {
    var body: some View {
        Grid(horizontalSpacing: 0, verticalSpacing: 0) {
            ForEach(0..<8) { row in
                GridRow {
                    ForEach(0..<8) { col in
                        if (row + col).isMultiple(of: 2) {
                            Rectangle()
                                .fill(.black)
                        } else {
                            Rectangle()
                                .fill(.white)
                        }
                    }
                }
            }
        }
        .aspectRatio(1, contentMode: .fit)
        .border(.black, width: 1)
        .padding()
    }
}

Download this as an Xcode projectopen in new window

Similar solutions…
How to position views in a grid using LazyVGrid and LazyHGrid | SwiftUI by Example

How to position views in a grid using LazyVGrid and LazyHGrid
How to make a fixed size Spacer | SwiftUI by Example

How to make a fixed size Spacer
How to adjust the position of a view using its offset | SwiftUI by Example

How to adjust the position of a view using its offset
How to add Metal shaders to SwiftUI views using layer effects | SwiftUI by Example

How to add Metal shaders to SwiftUI views using layer effects
How to style text views with fonts, colors, line spacing, and more | SwiftUI by Example

How to style text views with fonts, colors, line spacing, and more

이찬희 (MarkiiimarK)
Never Stop Learning.