Exploring the SwiftUI’s Grid View
LazyVGrid & GridItem and Grid & GridRow
Introduction
In SwiftUI, we can create a two-dimensional responsive list using LazyVGrid
or LazyHGrid
views. If we want a vertical grid, we can use the LazyVGrid
view, and if we want a horizontal grid, we can use the LazyHGrid
view. These views allow us to create a grid of items that adapt to different orientations and screen sizes.
With the introduction of iOS 16
, apple introduces a new way to statically arrange views in two dimensions. We can also create a two-dimensional layout by initializing a Grid
with a collection of GridRow
structures.
This article will cover the following topics :-
- Fundamentals of grid-based layouts in SwiftUI using
LazyVGrid
- Configure the layout of items in a lazy grid using
GridItem
withGridItem(.adaptive(minimum: 50, maximum: 100))
,GridItem(.fixed(50))
andGridItem(.flexible())
- A new way to statically arrange views in two dimensions using
Grid
andGridRow
. Customizing Grid usingMulticolumn cells
,Cell spacing
andalignment
.
- Why Grid ?
- Grid Customization withgridCellUnsizedAxes(_:)
,gridCellColumns(_:)
,gridColumnAlignment(_:)
,gridCellAnchor(_:)
Let’s start with a basic example of using LazyVGrid:
struct ContentView: View {
let data = Array(1...50)
var columns: [GridItem] = [
GridItem(.fixed(50)),
GridItem(.fixed(50)),
GridItem(.fixed(50))
]
var body: some View {
ScrollView {
LazyVGrid(columns: self.columns) {
ForEach(data, id: \.self) { number in
Text("\(number)")
.frame(width: 50, height: 50)
.background(Color.orange)
.foregroundColor(Color.black)
.cornerRadius(25)
}
}
}
.padding()
}
}
In the above example, we create a grid of Text
numbered from 1 to 50 using LazyVGrid
. First, defined a number of columns with GridItem
. Here, we define three columns with a fixed size of width 50 by setting .fixed(50)
.
Then, we put the grid view inside a scroll view to make it scrollable. And last, specified columns
as an argument for LazyVGrid
. That’s all we need to do to create a grid layout in SwiftUI.
GridItem
GridItem is basically a description of a row or a column in a lazy grid. To configure the layout of items in a lazy grid, we use an array of GridItem
instances (columns in case of LazyVGrid).
Each grid item in the array specifies layout properties like size and spacing for the columns of a LazyVGrid
or the rows of a LazyHGrid
. We will look about spacing in grid item later on.
// Minimum item width of 50
GridItem(.adaptive(minimum: 50))
// Fixed item width of 50
GridItem(.fixed(50))
// Flexible item width
GridItem(.flexible())
If we want the grid to fit in as many items per row as possible, we can use the adaptive()
size modifier. By setting .adaptive(minimum: 50)
, we will have the multiple number of Text
items in a row with a minimum size of 50 points each.
struct ContentView: View {
let data = Array(1...50)
var columns: [GridItem] = [
GridItem(.adaptive(minimum: 50)) // Minimum item width of 50
]
var body: some View {
ScrollView {
LazyVGrid(columns: self.columns) {
ForEach(data, id: \.self) { number in
Text("\(number)")
.frame(width: 50, height: 50)
.background(Color.orange)
.foregroundColor(Color.black)
.cornerRadius(25)
}
}
}
.padding()
}
}
If we want to control the number of items in each column, we can use the flexible()
size modifier. By using the flexible()
modifier, we can control the number of items in each column. However, if there isn’t enough space to accommodate the minimum size of the view, items may overlap.
struct ContentView: View {
let data = Array(1...50)
var columns: [GridItem] = [
GridItem(.flexible(minimum: 50, maximum: 100)),
GridItem(.flexible(minimum: 50, maximum: 100)),
GridItem(.flexible(minimum: 50, maximum: 100))
]
var body: some View {
ScrollView {
LazyVGrid(columns: self.columns) {
ForEach(data, id: \.self) { number in
Text("\(number)")
.frame(width: 50, height: 50)
.background(Color.orange)
.foregroundColor(Color.black)
.cornerRadius(25)
}
}
}
.padding()
}
}
⚠️ We can control the item width of
LazyVGrid
, but the height is determined by the highest item in that particular row. Similarly, we can control the item height ofLazyHGrid
, but the width is determined by the widest item in that particular column.
Spacing in Grid Item
spacing
define spacing to the next item. If this value is nil
, the item uses a reasonable default for the current platform.
Here is an example of a five-column grid where the spacing between each item in a column is 0, 10, 20, and 30, respectively.
Note that the spacing defined in the last column (40) has no effect since there is no next item.
struct ContentView: View {
let data = Array(1...50)
var columns: [GridItem] = [
GridItem(.fixed(50), spacing: 0),
GridItem(.fixed(50), spacing: 10),
GridItem(.fixed(50), spacing: 20),
GridItem(.fixed(50), spacing: 30),
GridItem(.fixed(50), spacing: 40)
]
var body: some View {
ScrollView {
LazyVGrid(columns: self.columns) {
ForEach(data, id: \.self) { number in
Text("\(number)")
.frame(height: 50)
.frame(maxWidth: .infinity)
.background(Color.orange)
.foregroundColor(Color.black)
.cornerRadius(25)
}
}
}
.padding()
}
}
Alignment in Grid Item
The alignment to use when placing each view. Use this property to anchor the view’s relative position to the same relative position in the view’s assigned grid space.
By default, each item will center align within a row/column. We can change this with the alignment
property.
Here is an example of a six-column grid where the alignment
of each item is set to default
, top
, center
, bottom
, topLeading
and bottomTrailing
respectively.
struct ContentView: View {
let data = Array(1...50)
var columns: [GridItem] = [
GridItem(.fixed(60)),
GridItem(.fixed(80), alignment: .top),
GridItem(.fixed(80), alignment: .center),
GridItem(.fixed(80), alignment: .bottom),
GridItem(.fixed(80), alignment: .topLeading),
GridItem(.fixed(80), alignment: .bottomTrailing),
]
var body: some View {
ScrollView {
LazyVGrid(columns: self.columns) {
ForEach(data, id: \.self) { number in
Text("Item \(number)")
.foregroundColor(Color.black)
.padding(8)
.background(Color.orange)
.border(.black)
}
}
}
.padding()
}
}
Grid and GridRow
With the introduction of iOS 16
, apple introduces a new way to statically arrange views in two dimensions. Create a two-dimensional layout by initializing a Grid
with a collection of GridRow
structures. The first view in each grid row appears in the grid’s first column, the second view in the second column, and so on.
Let’s start with a basic example that creates a grid with two rows and two columns:
struct ContentView: View {
var body: some View {
Grid {
GridRow {
Text("Hello")
Image(systemName: "globe")
}
GridRow {
Image(systemName: "hand.wave")
Text("World")
}
}
}
}
Multicolumn cells
The GridRow
controls the number of rows we want to display in the Grid View. Each element within the GridRow
corresponds to a column element. If you create rows with different numbers of columns, the grid adds empty cells to the trailing edge of rows that have fewer columns. The example below creates three rows with different column counts:
struct ContentView: View {
var body: some View {
Grid {
GridRow {
Text("Row 1")
ForEach(0..<2) { _ in Color.red }
}
GridRow {
Text("Row 2")
ForEach(0..<5) { _ in Color.green }
}
GridRow {
Text("Row 3")
ForEach(0..<4) { _ in Color.orange }
}
}
.padding()
}
}
Cell spacing and alignment
We can control the spacing between cells in both the horizontal and vertical dimensions, and set a default alignment for the content in all the grid cells when we initialize the grid. Consider a modified version of the previous example:
Grid(alignment: .bottom, horizontalSpacing: 20, verticalSpacing: 2) {
// ...
}
Why Grid ?
You may be wondering by now that what’s the use of Grid
if we already have LazyVGrid
and LazyHGrid
. Till now, we have only seen the basics of Grid
which have same functionalities as of LazyVGrid and LazyHGrid.
But, what’s more in Grid
? Grid
enables us to organize views in a table-like structure, similar to what we do in web. We can merge cells or columns, create blank cells, set cell spacing, and control alignment and other things. Following are the available methods for achieving this. Let see all of them one by one.
gridCellUnsizedAxes(_:)
gridCellColumns(_:)
gridColumnAlignment(_:)
gridCellAnchor(_:)
gridCellUnsizedAxes(_:)
Use this modifier to prevent a flexible view from taking more space on the specified axes than the other cells in a row or column require.
If we add an item inside Grid view without GridRow
, it will expand to a full column, i.e. it will takes as much space as its parent offers.
struct ContentView: View {
var body: some View {
Grid {
GridRow {
Text("Hello")
Image(systemName: "globe")
}
// This Divider will takes as much space as its parent offers
Divider()
GridRow {
Image(systemName: "hand.wave")
Text("World")
}
}
}
}
Here, how we can prevent the grid from giving the divider more width than the other cells require by adding the gridCellUnsizedAxes
modifier:
Divider()
.gridCellUnsizedAxes(.horizontal)
We can also utilize gridCellUnsizedAxes
to create an empty cell in a grid.
struct NContentView: View {
var body: some View {
Grid {
GridRow {
ForEach(0..<3) { _ in Rectangle().foregroundColor(.red) }
}
GridRow {
Rectangle().foregroundColor(.green)
Rectangle()
.foregroundColor(.clear)
.gridCellUnsizedAxes([.horizontal, .vertical])
Rectangle().foregroundColor(.green)
}
GridRow {
ForEach(0..<3) { _ in Rectangle().foregroundColor(.orange) }
}
}
.padding()
}
}
gridCellColumns(_:)
By default, each view that you put into the content closure of a GridRow
corresponds to exactly one column of the grid. If you want a view to span more than one column, you can specify that using gridCellColumns(_:)
modifier.
struct NContentView: View {
var body: some View {
Grid {
GridRow {
ForEach(0..<3) { _ in Rectangle().foregroundColor(.red) }
}
GridRow {
Rectangle().foregroundColor(.green)
Text("This text will span to two columns")
.gridCellColumns(2) // Span two columns.
}
GridRow {
ForEach(0..<3) { _ in Rectangle().foregroundColor(.orange) }
}
}
.padding()
}
}
gridColumnAlignment(_:)
We can use the gridColumnAlignment(_:)
modifier to override the horizontal alignment of a column within the grid. It causes all cells in the same column of a grid to use the same alignment.
struct ContentView: View {
var body: some View {
Grid {
GridRow {
Rectangle().foregroundColor(.green)
// Align the entire second column.
Text("Trailing text")
.gridColumnAlignment(.trailing)
Rectangle().foregroundColor(.green)
}
GridRow {
Text("Row 2, Col 1")
Text("Row 2, Col 2")
Text("Row 2, Col 3")
}
GridRow {
ForEach(0..<3) { _ in Rectangle().foregroundColor(.orange) }
}
}
.padding()
}
}
gridCellAnchor(_:)
When we use the gridCellAnchor(_:)
modifier on a view in a grid, the grid changes to an anchor-based alignment strategy for the associated cell. With anchor alignment, the grid projects a UnitPoint
that we specify onto both the view and the cell, and aligns the two projections.
For example, consider the following grid:
struct ContentView: View {
var body: some View {
Grid {
GridRow {
Rectangle().foregroundColor(.green).frame(width: 100, height: 100)
Rectangle().foregroundColor(.green).frame(width: 100, height: 100)
}
GridRow {
Rectangle().foregroundColor(.orange).frame(width: 100, height: 100)
Rectangle()
.foregroundColor(.orange)
.frame(width: 20, height: 20)
.gridCellAnchor(UnitPoint(x: 0.25, y: 0.75))
}
}
.padding()
}
}
With the anchor modifier shown in the code above, the grid aligns the one quarter point of the marker with the one quarter point of its cell in the x direction, as measured from the origin at the top left of the cell. The grid also aligns the three quarters point of the marker with the three quarters point of the cell in the y direction.
We can also use the typical alignment guides for gridCellAnchor
modifier. Consider a modified version of the previous example:
Rectangle()
.foregroundColor(.orange)
.frame(width: 20, height: 20)
.gridCellAnchor(.topTrailing)
👉 Applying the anchor-based alignment strategy to a single cell doesn’t affect the alignment strategy that the grid uses on other cells.
Conclusion
Grid view render all of its child views all at once. If you want better performance of your app, you can use
LazyHGrid
orLazyVGrid
.
Thanks for Reading!
Hope, you have found this article useful. If you liked this article, please share and 👏 so other people can read it too.
For reading & learning about these topics, I found the Apple’s documentation to be very useful. Here are the reference for
Grid
view andLazyVGrid
&LazyHGrid
.