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:
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" }
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
GridItem
s, 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:
Similarly, I've tried .lineLimit(1)
as well, but this results in truncation:
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
GridItem
s?