Updated for Xcode 14.2
SwiftUI’s LazyVGrid
and LazyHGrid
give us grid layouts with a fair amount of flexibility. The simplest possible grid is made up of three things: your raw data, an array of GridItem
describing the layout you want, and either a LazyVGrid
or a LazyHGrid
that brings together your data and your layout.
For example, this will create a vertical grid layout using cells that are 80 points in size:
struct ContentView: View {
let data = (1...100).map { "Item \($0)" }
let columns = [
GridItem(.adaptive(minimum: 80))
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
Download this as an Xcode project
Using GridItem(.adaptive(minimum: 80))
means we want the grid to fit in as many items per row as possible, using a minimum size of 80 points each.
If you wanted to control the number of columns you can use .flexible()
instead, which also lets you specify how big each item should be but now lets you control how many columns there are. For example, this creates five columns:
struct ContentView: View {
let data = (1...100).map { "Item \($0)" }
let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
Download this as an Xcode project
A third option is to use fixed sizes. For example, this will make the first column be exactly 100 points wide, and allow the second column to fill up all the remaining space:
struct ContentView: View {
let data = (1...100).map { "Item \($0)" }
let columns = [
GridItem(.fixed(100)),
GridItem(.flexible()),
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
ForEach(data, id: \.self) { item in
Text(item)
}
}
.padding(.horizontal)
}
.frame(maxHeight: 300)
}
}
Download this as an Xcode project
You can also use LazyHGrid
to make a horizontally scrolling grid, which works in much the same way except it accepts rows in its initializer.
For example, we could create 10 side by side heading images that are horizontally scrolling like this:
struct ContentView: View {
let items = 1...50
let rows = [
GridItem(.fixed(50)),
GridItem(.fixed(50))
]
var body: some View {
ScrollView(.horizontal) {
LazyHGrid(rows: rows, alignment: .center) {
ForEach(items, id: \.self) { item in
Image(systemName: "\(item).circle.fill")
.font(.largeTitle)
}
}
.frame(height: 150)
}
}
}
Download this as an Xcode project
As you can see, the code required to create horizontal and vertical grids is almost the same, changing just rows
for columns
.
If you’re required to support iOS 13 you won’t have access to LazyHGrid
or LazyVGrid
, so read below for an alternative…
SwiftUI gives us VStack
for vertical layouts and HStack
for horizontal layouts, but nothing that does both – nothing that can lay out views in a grid structure.
Fortunately we can write one ourselves by leveraging SwiftUI’s view builder system. This means writing a type that must be created using a row and column count, plus a closure it can run to retrieve the views for a given cell in the grid. Inside the body it can then loop over all the rows and columns and create cells inside VStack
and HStack
to make a grid, each time calling the view closure to ask what should be in the cell.
In code it looks like this:
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
let content: (Int, Int) -> Content
var body: some View {
VStack {
ForEach(0 ..< rows, id: \.self) { row in
HStack {
ForEach(0 ..< columns, id: \.self) { column in
content(row, column)
}
}
}
}
}
init(rows: Int, columns: Int, @ViewBuilder content: @escaping (Int, Int) -> Content) {
self.rows = rows
self.columns = columns
self.content = content
}
}
// An example view putting GridStack into practice.
struct ContentView: View {
var body: some View {
GridStack(rows: 4, columns: 4) { row, col in
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}
}
}
Download this as an Xcode project
That creates a 4x4 grid with an image and text in each cell.
SPONSORED From March 20th to 26th, you can join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.