LazyVGrid and LazyHGrid in SwiftUI iOS 03.06.2021

SwiftUI offers two new views LazyVGrid and LazyHGrid to show views in a grid layout that expands either vertically or horizontally. The word Lazy means that the grid view does not create items until they are needed. Thous views are available from iOS 14 and upward.

Here are the steps to create a grid layout.

First, prepare the raw data to present in the grid.

var colors: [Color] = [.orange, .green, .gray]

Next, create an array of GridItem that describes how the grid looks like. Here is a sample code snippet for describing a 2-column grid:

var grid = [GridItem(.flexible()), GridItem(.flexible())]

Finally, layout the grid by using LazyVGrid and ScrollView.

ScrollView {
    LazyVGrid(columns: threeColumnGrid) {
        // Display the item
    }
}

You use GridItem instances to configure the layout of items in LazyHGrid and LazyVGrid views. Grid item is either row or column.

GridItems are declared using the following syntax

GridItem(sizing, spacing, alignment)

We have three options to specify the size of the grid item

  • flexible(minimum: CGFloat, maximum: CGFloat) specifies single flexible item in available space with optional parameters for min and max size.
  • adaptive(minimum: CGFloat, maximum: CGFloat) specifies the size of the row or column is adjusted to fit as many items as possible into the available space.
  • fixed(CGFloat) specifies a single item with a fixed size in available space.

Flexible

A flexible grid item lets you specify a range of sizes while still defining the number of columns. We can provide minimum and maximum, but it works great even without that. The item size is calculated by dividing available space by item count.

struct ContentView: View {    
    private var colors: [Color] = [.orange, .green, .gray]
    private var items = [GridItem(.flexible(minimum: 60)), GridItem(.flexible()), GridItem(.flexible())]

    struct ItemView: View {
        var index: Int        
        var color: Color
        var body: some View {
            Text("\(index)")
                .frame(minWidth: 50, maxWidth: .infinity, minHeight: 100)
                .background(color)
                .cornerRadius(4)
        }
    }

    var body: some View {
        ScrollView {
            LazyVGrid(columns: items, spacing: 4) {
                ForEach((0...10), id: \.self) { index in
                    ItemView(index: index, color: colors[index % colors.count])
                }
            }
            .padding(4)
        }
    }
}

Adaptive

Adaptive grid layout size is the most suitable and appropriate GridItem size. It fills the space with as many columns or rows as fit in the grid. To use adaptive sizing, modify the items array to contain a single adaptive item as follows:

private var items = [GridItem(.adaptive(minimum: 80))]

This change will result in the grid displaying as many columns as possible with the restriction that the column width cannot be less than 80dp.

Fixed

Fixed grid layout size allows rows or columns to be set at a specific size. When using only fixed GridItems in the array passed to the grid view, the number of GridItems will set the number of rows or columns. For example, the following array, when passed to a LazyVGrid view, will display a grid containing a single column with a width of 80dp.

private var items = [GridItem(.fixed(80))]

On the other hand, the following array will display a three column grid with the columns sized at 30dp, 40dp and 50dp respectively:

private var items = [GridItem(.fixed(30)), GridItem(.fixed(40)), GridItem(.fixed(50))]

When working with grids it is also possible to combine GridItem sizing configurations. For example, the following array, , will display the first column of each row with a fixed width with the second and third columns sized equally to occupy the remaining space:

private var items = [GridItem(.fixed(30)), GridItem(), GridItem()]

Using the LazyHGrid View

Horizontal grids work in much the same way as vertically oriented grids with the exception that the configuration is based on rows instead of columns, and that the fixed, minimum and maximum values relate to row height instead of column width.

struct ContentView: View {
    private var colors: [Color] = [.orange, .green, .gray]
    private var items = [GridItem(.adaptive(minimum: 40))]

    struct ItemView: View {
        var index: Int

        var color: Color
        var body: some View {
            Text("\(index)")
                .frame(minWidth: 50, minHeight: 50, maxHeight: .infinity)
                .background(color)
                .cornerRadius(4)
        }
    }

    var body: some View {
        ScrollView(.horizontal) {
            LazyHGrid(rows: items, spacing: 4) {
                ForEach((0...50), id: \.self) { index in
                    ItemView(index: index, color: colors[index % colors.count])
                }
            }
            .padding(4)
        }
    }
}