How do GridItem sizes (fixed, flexible, adaptive) and Grid differ in how they size columns?

8.12.4 · L3 · Interview

Predict first

What widths and positions do the cells get in a 320-point container?

@MainActor func run(_ label: String, _ v: some View) {
    frames = [:]
    _ = show(Root(content: v.frame(width: 320, height: 300, alignment: .topLeading)))
    print(label, (0..<4).map { f("c\($0)") }.joined(separator: " | "))
}
run("flexible x2:", LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 0) {
    ForEach(0..<4) { Color.red.frame(height: 10).measure("c\($0)") } })
run("fixed 60 + flexible:", LazyVGrid(columns: [GridItem(.fixed(60), spacing: 0), GridItem(.flexible(), spacing: 0)], spacing: 0) {
    ForEach(0..<4) { Color.red.frame(height: 10).measure("c\($0)") } })
run("adaptive min 100:", LazyVGrid(columns: [GridItem(.adaptive(minimum: 100), spacing: 0)], spacing: 0) {
    ForEach(0..<4) { Color.red.frame(height: 10).measure("c\($0)") } })
run("Grid:", Grid(horizontalSpacing: 0, verticalSpacing: 0) {
    GridRow { Color.red.frame(width: 30, height: 10).measure("c0"); Color.red.frame(width: 50, height: 10).measure("c1") }
    GridRow { Color.red.frame(width: 80, height: 10).measure("c2"); Color.red.frame(width: 20, height: 10).measure("c3") } })
  • flexible 156/156, fixed 60/260, adaptive three columns of 107, Grid columns 80 and 50
  • all grids give equal columns
  • Grid ignores content width

Mental model. LazyVGrid is a printed table with pre-drawn columns; Grid is a table whose columns are measured after the content is written in.

LazyVGrid takes an array of GridItem column descriptions and lays children into them row by row. .flexible() columns share the width left over (two of them in 320 points with the default 8-point spacing are 156 wide each); .fixed(60) is exactly 60 and the flexible column takes the other 260; .adaptive(minimum: 100) is one description that expands into as many columns as fit: in 320 points it made three columns of about 107. Column widths come from the descriptions, not from the content, so a cell that wants more room is clipped or squeezed. The grid is lazy (cells are created when scrolled in), so it needs a scroll view and unknown total height.

Grid (iOS 16) is different: not lazy, and the column width is the widest cell in that column. In the check the first column is 80 wide (from the 80-wide cell) and the second is 50, with smaller cells centred in their columns (the 30-wide cell sits at x = 25, the 20-wide one at x = 95). Use Grid for small tables and forms where content decides the widths, and LazyVGrid for large collections where the layout decides. For different cell sizes or true masonry layouts, write a custom Layout.

Lazy grids use column descriptions; Grid uses content.

Common traps

  • Using LazyVGrid for a handful of cells that need content-driven widths. Use Grid.
  • Expecting .adaptive to size by content. It only counts how many columns of the minimum width fit.

They might follow up with

  • How do you align cells in a Grid? gridColumnAlignment and the grid’s alignment.
  • Is Grid lazy? No.

Related questions

  • 8.6.1 How do List, ForEach and the lazy stacks differ, and what do they need from your data?
  • 8.5.3 How does the Layout protocol work, and when is a custom layout the right tool?
  • 8.12.3 How do ViewThatFits and AnyLayout differ, and which one preserves state when the arrangement changes?

← How do ViewThatFits and AnyLayout differ, and which one preserves…