SwiftUI 4.0 introduces a new Grid API for composing grid-based layout. You can arrange the same layout by using VStack and HStack. The Grid view, however, makes it a lot easier. A Grid view is a container view that arranges other views in a two dimensional layout.
Let's start with a simple grid. To create a 2x2 grid, you write the code like below:
struct ContentView: View {
var body: some View {
Grid {
GridRow {
Color.purple
Color.orange
}
GridRow {
Color.green
Color.yellow
}
}
}
}
Assuming you've created a SwiftUI project in Xcode, you should see a 2x2 grid, filled with different colors.

To create a 3x3 grid, you just need to add another GridRow. And, for each GridRow, you insert one more child view.

You may wonder why we need to use the Grid APIs. You can create the same grid UI using HStack and VStack. So, why did Apple introduce this new APIs? It's correct that you can build the same grid with stack views. However, there is a major difference that makes Grid views more desirable in building grid layout.
Let's create another 2x2 grid using the code below:
struct ContentView: View {
var body: some View {
Grid {
GridRow {
Image(systemName: "trash")
.font(.system(size: 100))
Image(systemName: "trash")
.font(.system(size: 50))
}
GridRow {
Color.green
Color.yellow
}
}
}
}
This time, the first row displays two system images and the second row shows the color views. Your preview should show a grid like below.

To build the same grid layout using nested VStack and HStack, we can write the code like this:
VStack {
HStack {
Image(systemName: "trash")
.font(.system(size: 100))
.frame(minWidth: 0, maxWidth: .infinity)
Image(systemName: "trash")
.font(.system(size: 50))
.frame(minWidth: 0, maxWidth: .infinity)
}
HStack {
Color.green
Color.yellow
}
}
By default, each column of the Grid view occupies equal space in a row. But for HStack, we have to attach the frame modifier to make each image take up the same space across the row.
The Grid view just makes it easier to create grid views and provides several modifiers to customize the grid layout.
When creating a grid view, you may want to display a single cell in a specific row, while other rows keep showing multiple cells. The gridCellColumns modifier is designed for you to merge cells. Here is an example:
Grid {
GridRow {
Image(systemName: "trash")
.font(.system(size: 100))
Image(systemName: "trash")
.font(.system(size: 50))
}
GridRow {
Color.purple
.overlay {
Image(systemName: "magazine.fill")
.font(.system(size: 100))
.foregroundColor(.white)
}
.gridCellColumns(2)
}
}
The second row only has one cell. We attach the gridCellColumn modifier and set its value to 2 to merge the cells. If you do not use the modifier, you'll see a blank cell.


Now let's create another 3x3 grid view like this:
struct ContentView: View {
var body: some View {
Grid {
GridRow {
IconView(name: "cloud")
IconView(name: "cloud")
IconView(name: "cloud")
}
GridRow {
IconView(name: "cloud")
IconView(name: "cloud")
IconView(name: "cloud")
}
GridRow {
IconView(name: "cloud")
IconView(name: "cloud")
IconView(name: "cloud")
}
}
}
}
struct IconView: View {
var name: String = "trash"
var body: some View {
Image(systemName: name)
.frame(width: 100, height: 100)
.background(in: Rectangle())
.backgroundStyle(.purple)
.foregroundStyle(.white.shadow(.drop(radius: 1, y: 3.0)))
.font(.system(size: 50))
}
}
What if we want to display a blank view for the cell at the center of the grid? To make this happen, you can use the following code to add a blank cell:
Color.clear
.gridCellUnsizedAxes([.horizontal, .vertical])
If you replace the center cell with the code above, you will see a blank cell at the center of the grid.

The gridCellUnsizedAxes modifier prevents the blank cell from taking up more space than the other cells in the row or column need?. If you omit the modifier, you will achieve a grid layout like figure 7.

To control the vertical and horizontal spacing between cells, you can use the horizontalSpacing and verticalSpacing parameter when instantiating a Grid view:
Grid(horizontalSpacing: 0, verticalSpacing: 0) {
.
.
.
}
If you need to add some spacing between rows, you can attach the padding modifier to GridRow. Figure 8 shows you an example.

The Grid view has an optional parameter named alignment for you to configure the default alignment of the cells. For example, here sets the default alignment to .bottom:
Grid(alignment: .bottom, horizontalSpacing: 0, verticalSpacing: 0) {
.
.
.
}
If you've changed the code, the black box should align to the bottom of the cell.

To override the default alignment setting, the cell itself can attach the gridCellAnchor modifier to change the alignment. Figure 10 shows an example.

The introduction of the Grid APIs provides developers with one more option to build grid-based layout. You can use HStack and VStack to build a similar layout. That said, Grid views save you quite a lot of code and make your code more readable. If your app only needs to support the latest version of iOS , give the Grid API a try and build some interesting layouts.
For reference, you can download the complete project here: