Chapters

Hide chapters

SwiftUI by Tutorials

Fifth Edition · iOS 16, macOS 13 · Swift 5.8 · Xcode 14.2

Before You Begin

Section 0: 4 chapters
Show chapters Hide chapters

16. Grids
Written by Bill Morefield

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

Stacks and lists provide a one-dimensional arrangement of views. Many data types better fit a two-dimensional grid. A significant weakness of the initial release of SwiftUI was the lack of a native collection view. This view is so helpful that the first two editions of this book included a chapter that walked through creating a reusable grid view. SwiftUI 2.0 added a native grid view. In this chapter, you’ll examine and work with grid layouts in SwiftUI.

Building Grids the Original Way

The containers in the original SwiftUI version that let you organize other views shared one thing in common; they work in one dimension. Stacks create horizontal or vertical layouts. Lists create vertical layouts.

You can think of a grid as a set of stacks in one direction wrapped within a stack of the other direction. Because of this, you can create more complex layouts, even with these limitations. You just had to do the work yourself.

Open the starter project and run the app. Earlier editions of this book changed the buttons on the welcome screen to a grid, but that no longer fits the new split navigation design (see Chapter 13: Navigation). Instead, you’ll explore grids using the new awards view added to the starter project. Tap the Your Awards button to look at the initial view.

Initial Awards View
Initial Awards View

This view displays the user’s current awards and those the user hasn’t received yet. Currently, the list shows as a vertical stack using a LazyVStack. Open AwardsView.swift under the AwardsView group and change the closure of the ScrollView to:

// 1
VStack {
  // 2
  HStack {
    NavigationLink(value: awardArray[0]) {
      AwardCardView(award: awardArray[0])
        .foregroundColor(.black)
        .frame(width: 150, height: 220)
    }
    Spacer()
    NavigationLink(value: awardArray[1]) {
      AwardCardView(award: awardArray[1])
        .foregroundColor(.black)
        .frame(width: 150, height: 220)
    }
  }
  // 3
  HStack {
    AwardCardView(award: awardArray[2])
      .foregroundColor(.black)
      .frame(width: 150, height: 220)
    Spacer()
    AwardCardView(award: awardArray[3])
      .foregroundColor(.black)
      .frame(width: 150, height: 220)
  }
  Spacer()
}
.font(.title)
.foregroundColor(.white)
.padding()

That’s a lot of code but focus on the layout views. You’ll see that you’re building a grid by nesting two HStacks inside a VStack to show the first four awards.

  1. Using an initial VStack creates the overall vertical layout of the grid.
  2. This HStack builds the first row of the grid. It contains two of the button views separated by a Spacer. Note that you manually reference the elements of the awardArray array.
  3. This HStack makes the second row of the grid.

Run the app, and you’ll see the grid.

Manual Grid
Manual Grid

Before the Grid view, this technique was the only way to build a grid. You can see how complex it becomes as you add more items to the grid. This book’s first edition included a chapter on creating a generic reusable grid that you can consult if you want to see more of this technique. With the second release of SwiftUI, there’s now a native and more flexible way to build a grid. You’ll change the app to use that in the next section.

Creating a Fixed Column Grid

The native SwiftUI grid view builds on the existing LazyHStack and LazyVStack views. As with stacks, there are two grids, one that grows horizontally and one that grows vertically. Change the contents of the ScrollView in AwardsView.swift to:

// 1
LazyVGrid(
  // 2
  columns:
    [
      // 3
      GridItem(.fixed(160)),
      GridItem(.fixed(160))
    ],
  // 4
  spacing: 15
) {
  // 5
  ForEach(awardArray) { award in
    // 6
    NavigationLink(value: award) {
      AwardCardView(award: award)
        .foregroundColor(.black)
        // 7
        .frame(width: 150, height: 220)
    }
  }
}
.navigationDestination(for: AwardInformation.self) { award in
  AwardDetails(award: award)
}
.font(.title)
.foregroundColor(.white)
.padding()
Awards Shown in a Grid
Ufeypy Xpoxb ej i Ssej

Building Flexible Grids

You often want more flexibility when creating columns (or rows) in your grid. A flexible element in a grid lets you specify a range of sizes to constrain a grid while setting the number of rows or columns in the grid. Placing the column information inside the view clutters its view, especially when your grid becomes more complicated. Instead, you will specify the column structure using a property. Add the following code after the awardArray property:

var awardColumns: [GridItem] {
  [
    GridItem(.flexible(minimum: 150)),
    GridItem(.flexible(minimum: 150))
  ]
}
LazyVGrid(columns: awardColumns, spacing: 15) {
  ForEach(awardArray) { award in
    NavigationLink(value: award) {
      AwardCardView(award: award)
        .foregroundColor(.black)
        .frame(width: 150, height: 220)
    }
  }
}
Flexible grid screen
Mxutuscu dqoz rsvoad

Interacting Between Views and Columns

It’s worth exploring how the container view’s size interacts with the settings for columns in the grid. Change the frame for the award card to:

.frame(width: 190, height: 220)
Flexible grid with larger cards
Rlesalve fpic lujs najmiy cecct

var awardColumns: [GridItem] {
  [
    GridItem(.flexible(minimum: 100, maximum: 150)),
    GridItem(.flexible(minimum: 100, maximum: 150))
  ]
}
.frame(width: 160, height: 240)
Award grid still clipped
Enehj vxev rnizb zvugniw

var awardColumns: [GridItem] {
  [
    GridItem(.flexible(minimum: 100, maximum: 160)),
    GridItem(.flexible(minimum: 100, maximum: 160))
  ]
}
AwardCardView(award: award)
  .foregroundColor(.black)
  .aspectRatio(0.67, contentMode: .fit)
Award grid screen with size by aspect ratio
Owuxf rwos hwgaix fiws mimo vv ilhonx gekoa

Award grid in horizontal mode with 2 columns
Uyokm dtun uc cocovaskum feyu vobx 2 newewjq

Building Adaptive Grids

The adaptive grid provides you the most flexible option. Using one tells SwiftUI to fill the space with as many columns or rows as fit in the grid. Change the awardColumns property to:

var awardColumns: [GridItem] {
  [GridItem(.adaptive(minimum: 150, maximum: 170))]
}
Adaptive award grid in vertical
Ukiffole isosm jxaj aw mednefos

Adaptive award grid in horizontal
Iyekfema erogs dxov ih tasiqedquh

Award grid iPad
Imazm qsar uTuf

Using Sections in Grids

To help the user understand what award they have yet to receive, you’ll divide the awarded and not-awarded items into separate sections. Add two new computed properties below the awardArray property:

var activeAwards: [AwardInformation] {
  awardArray.filter { $0.awarded }
}

var inactiveAwards: [AwardInformation] {
  awardArray.filter { !$0.awarded }
}
struct AwardGrid: View {
  // 1
  var title: String
  var awards: [AwardInformation]

  var body: some View {
    // 2
    Section(
      // 3
      header: Text(title)
        .frame(maxWidth: .infinity)
        .font(.title)
        .foregroundColor(.white)
        .background(
          .ultraThinMaterial,
          in: RoundedRectangle(cornerRadius: 10)
        )
    ) {
      // 4
      ForEach(awards) { award in
        NavigationLink(value: award) {
          AwardCardView(award: award)
            .foregroundColor(.black)
            .aspectRatio(0.67, contentMode: .fit)
        }
      }
    }
  }
}
LazyVGrid(columns: awardColumns) {
  AwardGrid(
    title: "Awarded",
    awards: activeAwards
  )
  AwardGrid(
    title: "Not Awarded",
    awards: inactiveAwards
  )
}
Award grid with sections
Ixibb kweq migr vuyfiert

Key Points

  • SwiftUI provides two types of lazy loaded grids: LazyVGrid, which grows vertically and LazyHGrid, which grows horizontally.
  • You define columns for a LazyVGrid and rows for a LazyHGrid. A GridItem describes the layout for both types of grids.
  • A fixed grid item lets you specify an exact size for a column or row.
  • A flexible grid item lets you specify a range of sizes while still defining the number of columns.
  • An adaptive grid item can adapt to fill the available space in a view using provided size limits.
  • You can mix different types of grid items in the same row or column.

Where to Go From Here?

To see more about what creating grids required in the initial release of SwiftUI, see Chapter 20: Complex Interfaces in the second edition of this book.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now