New in SwiftUI 4: Grid View

DevTechie Inc
Jun 24, 2022


Photo by charlesdeluvio on Unsplash

SwiftUI 4 brought in a new container view called Grid view. A Grid is a container view which gives us a two-dimensional layout by arranging its child views in rows and columns.

We create a layout by initializing Grid with a collection of GridRows. Let’s create an example to understand better:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    Label("DevTechie", systemImage: "heart")
                    Label("iOS Development", systemImage: "graduationcap")
                }
            }
            .font(.title3)
        }
        .padding()
    }
}
We can add more grid rows like this:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    Label("DevTechie", systemImage: "heart")
                    Label("iOS Development", systemImage: "graduationcap")
                }
                GridRow {
                    Label("SwiftUI", systemImage: "swift")
                    Label("UIKit", systemImage: "gear")
                }
            }
            .font(.title3)
        }
        .padding()
    }
}
Let’s build a custom view for grid cells so we can use that view inside GridRow.

struct GridCell: View {
    
    let systemName: String
    let title: String
    
    var body: some View {
        HStack {
            Image(systemName: systemName)
            Text(title)
        }
    }
}
Let’s replace content inside GridRow with this custom view:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                    GridCell(systemName: "graduationcap", title: "iOS Development")
                }
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                    GridCell(systemName: "gear", title: "UIKit")
                }
            }
            .font(.title3)
        }
        .padding()
    }
}
Note: Grid and GridRows behave like a collection of HStack and VStack combined together, but Gridhandles row and column creation as a single operation which makes it efficient. While Grid is creating these rows and columns, it applies alignment and spacing to the cells.
Grid Cell Columns
gridCellColumns modifier tells view to span over specified number of rows.

For example, we can make DevTechie span over two columns with

.gridCellColumns(2)
Our complete code will look like this:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .font(.largeTitle)
                }
                .gridCellColumns(2)
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                    GridCell(systemName: "gear", title: "UIKit")
                }
            }
            .font(.title3)
        }
        .padding()
    }
}struct GridCell: View {
    
    let systemName: String
    let title: String
    
    var body: some View {
        HStack {
            Image(systemName: systemName)
            Text(title)
        }
    }
}
Styling Grid
We can style Grid using SwiftUI’s standard modifiers that we would use with any other view.

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .font(.largeTitle)
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(.orange.gradient)
                }
                .gridCellColumns(2)
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(.teal.gradient)
                    GridCell(systemName: "gear", title: "UIKit")
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(.indigo.gradient)
                }
            }
            .font(.title3)
            .foregroundColor(.white)
        }
        .padding()
    }
}
Alignment, Vertical and Horizontal Spacing
We can specify content alignment, vertical spacing, and horizontal spacing for grid cells via Grid’s overloaded function, which takes all these values as params.

We will remove frame with the maxWidth modifier to see this in action.

Alignment:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid(alignment: .leading){
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .font(.largeTitle)
                        .padding()
                        .background(.orange.gradient)
                }
                .gridCellColumns(2)
                
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                        .padding()
                        .background(.teal.gradient)
                    GridCell(systemName: "gear", title: "UIKit")
                        .padding()
                        .background(.indigo.gradient)
                    
                }
            }
            .font(.title3)
            .foregroundColor(.white)
        }
        .padding()
    }
}
Horizontal & Vertical Spacing:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid(alignment: .leading, horizontalSpacing: 0, verticalSpacing: 0){
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .font(.largeTitle)
                        .padding()
                        .background(.orange.gradient)
                }
                .gridCellColumns(2)
                
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                        .padding()
                        .background(.teal.gradient)
                    GridCell(systemName: "gear", title: "UIKit")
                        .padding()
                        .background(.indigo.gradient)
                    
                }
            }
            .font(.title3)
            .foregroundColor(.white)
        }
        .padding()
    }
}
Grid Cell Unsized Axes
gridCellUnsizedAxes modifier helps to prevent a flexible view from taking up more space on a given axis than is needed. To understand this better, we will add a Color view between the two rows. Because Color is a flexible view, it will try to take as much space as available:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .font(.largeTitle)
                        .padding()
                        .background(.orange.gradient)
                }
                .gridCellColumns(2)
                
                Color.blue
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                        .padding()
                        .background(.teal.gradient)
                    GridCell(systemName: "gear", title: "UIKit")
                        .padding()
                        .background(.indigo.gradient)
                    
                }
            }
            .font(.title3)
            .foregroundColor(.white)
        }
        .padding()
    }
}
In order to control this behavior we will apply gridCellUnsizedAxes modifier:

Color.blue.gridCellUnsizedAxes([.horizontal, .vertical])
Complete code will look like this:

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .font(.largeTitle)
                        .padding()
                        .background(.orange.gradient)
                }
                .gridCellColumns(2)
                
                Color.blue
                    .gridCellUnsizedAxes([.horizontal, .vertical])
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                        .padding()
                        .background(.teal.gradient)
                    GridCell(systemName: "gear", title: "UIKit")
                        .padding()
                        .background(.indigo.gradient)
                    
                }
            }
            .font(.title3)
            .foregroundColor(.white)
        }
        .padding()
    }
}
But our Color view disappeared 🤔. Well because its a flexible view, it needs a little more info, so we will set minHeightfor Color view to be 2 points.

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .font(.largeTitle)
                        .padding()
                        .background(.orange.gradient)
                }
                .gridCellColumns(2)
                
                Color.blue
                    .frame(minHeight: 2)
                    .gridCellUnsizedAxes([.horizontal, .vertical])
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                        .padding()
                        .background(.teal.gradient)
                    GridCell(systemName: "gear", title: "UIKit")
                        .padding()
                        .background(.indigo.gradient)
                    
                }
            }
            .font(.title3)
            .foregroundColor(.white)
        }
        .padding()
    }
}
Column
Grid column count grows as they grow for GridRows. So for example, one GridRow has 2 columns but another has 3 columns; then Grid will create 3 column space and fill first row’s last column with empty cell.

struct ContentView: View {
    var body: some View {
        VStack {
            Grid {
                GridRow {
                    GridCell(systemName: "heart", title: "DevTechie")
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(.orange.gradient)
                }
                
                GridRow {
                    GridCell(systemName: "swift", title: "SwiftUI")
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(.teal.gradient)
                    GridCell(systemName: "gear", title: "UIKit")
                        .frame(maxWidth: .infinity)
                        .padding()
                        .background(.indigo.gradient)
                    
                }
            }
            .font(.title3)
            .foregroundColor(.white)
        }
        .padding()
    }
}
This mean that resulting grid will have as many columns as the widest row. Width of all cells in a column matches the width of widest cell.

Performance Considerations
Grid creates a two-dimensional layout in a single operation which is faster than putting together HStack and VStack. Just like HStack or VStack, Grid renders all its child views immediately, so this mean that Grid is great for content that is small in size but if you are looking at rendering large content which needs scrollviews to scroll through, then consider using LazyVGrid or LazyHGrid as they would perform much efficiently.




With that we have reached the end of this article. Thank you once again for reading. Subscribe our newsletter at https://www.devtechie.com