1

I started looking at Paul Hudson's Hacking with Swift tutorial on positioning views in a grid. If I run the code given in the first listing, I get exactly what's shown in the corresponding screenshot. In other words, this code ...

import SwiftUI

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)
    }
}

... ran on an iPad 11 Pro Max simulator gets me the following: enter image description here

So far so good.

But I want to show a grid of Text views with a bit more content, so I change the first line of my view to ...

    let data = (1...100).map { "Item \($0) - more content" }

... but this gets me ... enter image description here

This is where my understanding falls apart. I would like to see a grid where each Text view is given the amount of horizontal space needed to accommodate the longest content, without line breaks or truncation. Something like this:

Item 1 - more content     Item 2 - more content
Item 3 - more content     Item 4 - more content
...
Item 42 - more content    Item 43 - more content
Item 44 - more content    Item 45 - more content
...
Item 142 - more content   Item 143 - more content
Item 144 - more content   Item 145 - more content
...
Item 7142 - more content  Item 7143 - more content
Item 7144 - more content  Item 7145 - more content
...

And if my data was long enough that strings were longer than half the width of the screen, I'd expect the grid to utilize just a single column.

Why don't the Text views take up more horizontal space? We've only specified a minimum for our adaptive GridItems, so why would they not expand horizontally as needed to accommodate the longest string? The documentation for GridItem.Size.adaptive says (emphasis mine):

This approach prefers to insert as many items of the minimum size as possible but lets them increase to the maximum size.

So how would I get the grid to let these views increase to their maximum size (which is .infinity by default)?

I've tried adding .fixedSize() to each Text, hoping it would force them to retain their ideal size. But this does not work: enter image description here

Similarly, I've tried .lineLimit(1) as well, but this results in truncation: enter image description here

I'm aware of three similar questions (here, here, here) but they are trying to achieve a variable line length with the given data. In my case I really do want a grid. I just want each grid item to take up as much horizontal space as the largest one. How could I achieve this?

I feel like I'm missing something simple about the way SwiftUI layout (or that of a grid) works. I know that during layout the parent view proposes a size to its children, and each child responds with its desired size. In this case it looks like the grid has already decided it will fit four columns on the screen, so it only offers roughly a quarter of its width to each Text. I don't follow why that would be the case when we've asked the grid items to be adaptive and only provided a minimum width.

In a "teach a man to fish ..." fashion, I'd greatly appreciate any advice or tooling that would help me understand how you arrive at your specific answer. For instance, is there a way for a developer to follow (debug print perhaps?) the proposals and counterproposals between parent and child views? Is there any documentation, explanation, etc. to learn more about what exactly goes on during the layout pass? Any detailed explanation of how exactly a grid decides on the sizes of adaptive GridItems?

Cem Schemel
  • 442
  • 3
  • 13
  • Paul explained your problem in his writeup "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." When you use `.adaptive`, SwiftUI will try to put as many columns in as it can. Hence the grid views becoming square in your first image. There is only so much screen real estate. If you use `.flexible`, you will gain resizing, but you have to account for the number of columns you want. Too many, and you will have the same issues as with `.adaptive`. – Yrb Apr 04 '22 at 14:10
  • @Yrb I realize that, but then why does the documentation for `.adaptive` say "This approach prefers to insert as many items of the minimum size as possible but lets them increase to the maximum size" if there's no condition under which they'll actually increase to maximum size? And if there is such a condition, what is it? – Cem Schemel Apr 04 '22 at 14:37
  • None that I know of with this setup. Remember, `GridItems` are mix and match. Generally, `.adaptive` is used as a final column so that things will fit in a space, after several `.fixed`. – Yrb Apr 04 '22 at 15:09

0 Answers0