Home iOS & Swift Books SwiftUI by Tutorials

15
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.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Several features didn’t make the initial release of SwiftUI. One of the most lamented ones was the lack of a native collection view. This view is so useful that earlier editions of this book included a chapter that walked through creating a reusable grid view.

The second release of SwiftUI made that chapter obsolete with the addition of 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 could create more complex layouts, even with these limitations. You just had to do the work yourself.

Open the starter project and run the app. You’ll see the buttons on the welcome screen now use a new vertical arrangement in a vertical stack.

Mountain Airport app initial screen
Mountain Airport app initial screen

With this new shape, the buttons would work better in a grid. First you’ll create a grid layout as you would in the inital version of SwiftUI. Open WelcomeView.swift and change the closure of the ScrollView to:

// 1
VStack {
  // 2
  HStack {
    FlightStatusButton(flightInfo: flightInfo)
    Spacer()
    SearchFlightsButton(flightInfo: flightInfo)
  }
  // 3
  HStack {
    AwardsButton()
    // 4
    LastViewedButton(
      flightInfo: flightInfo,
      appEnvironment: appEnvironment,
      showNextFlight: $showNextFlight
    )
  }
  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 an HStack inside a VStack.

  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.
  3. This HStack makes the second row of the grid.
  4. If the user hasn’t viewed a flight, LastViewedButton will be a Spacer to keep the number of elements in the two rows identical.

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

Manual Grid
Manual Grid

In the initial release of SwiftUI, this technique was the only way to build a grid. This book’s previous editions included a chapter on creating a generic reusable grid that you can consult if you’d like to see more on this technique. With the second release of SwiftUI, there’s now a native and more flexible option 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 control builds on top of 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 WelcomeView.swift to:

// 1
LazyVGrid(
  // 2
  columns: [
    // 3
    GridItem(.fixed(160)),
    GridItem(.fixed(160))
    // 4
  ], spacing: 15
) {
  FlightStatusButton(flightInfo: flightInfo)
  SearchFlightsButton(flightInfo: flightInfo)
  AwardsButton()
  LastViewedButton(
    flightInfo: flightInfo,
    appEnvironment: appEnvironment,
    showNextFlight: $showNextFlight
  )
}.font(.title)
.foregroundColor(.white)
.padding()
Welcome Grid screen
Wobpoco Gciy qhluud

Building flexible grids

A static grid works for many cases, but you have 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 also setting the number of rows or columns in the grid.

Awards screen
Ubodjb ttbuux

var awardColumns: [GridItem] {
  [GridItem(.flexible(minimum: 150)),
  GridItem(.flexible(minimum: 150))]
}
LazyVGrid(columns: awardColumns) {
  ForEach(awardArray, id: \.self) { award in
    NavigationLink(destination: AwardDetails(award: award)) {
      AwardCardView(award: award)
        .foregroundColor(.black)
        .frame(width: 150, height: 220)
    }
  }
}
Flexible grid screen
Xvohohvi wnam clcaov

Interacting between views and columns

It’s worth spending a moment 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
Vkuzurzu pdad zuwx voczuk navzq

var awardColumns: [GridItem] {
  [GridItem(.flexible(minimum: 150, maximum: 170)),
  GridItem(.flexible(minimum: 150, maximum: 170))]
}
Award grip clipped
Ebamn hdaf tyipbut

AwardCardView(award: award)
  .foregroundColor(.black)
  .aspectRatio(0.67, contentMode: .fit)
Award grid screen
Azatk qmis xsliux

Award grid in horizontal mode with 2 columns
Asard xrof ob yobujafney xiqu giqd 4 tobofch

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
Enuhzidi ejawh ytal ef ruqcezic

Adaptive award grid in horizontal
Evehhego ihels hjer el giqequpyig

Award grid iPad
Emord hfuy iNeq

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)
        .font(.title)
        .foregroundColor(.white)
    ) {
      // 4
      ForEach(awards, id: \.self) { award in
        NavigationLink(
          destination: AwardDetails(award: 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 finished screen
Umecb vzuy tetitsum mbfaiv

Key points

  • SwiftUI provides two types of 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 of 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 provides 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 creating grids required in the initial release of SwiftUI, see Chapter 15: 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.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

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 raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.