i'm writing a SwiftUI app and would like a container view which "pushes" its children into a row and, once the row fills up, starts wrapping subsequent children to the subsequent row—in other words, a container which emulates the behavior of CSS flexbox's flex-wrap: wrap
attribute. the children have variable, dynamic widths, but are the same height (although i suspect most non-insane solutions will be robust to variable heights). the number of children is also dynamic.
toplevel, my question is just: what's the simplest way to do this? if i need to dip into UIKit or obj-c land, so be it, but i'd prefer solutions which are standalone (i.e., not pod Yoga
).
(NB: i am new to iOS dev, but have working familiarity with React and with all the ~newfangled CSS layouts.)
so far my best lead is the LazyVGrid
View that ships with iOS 14. in particular, it seems to me like a single adaptive GridItem with no maximum (defaulting to infinity) should do the trick... but it does not, or at least i haven't figured out how yet. (the best reference slash only detailed reference for *Grid
i've found is: https://swiftui-lab.com/impossible-grids/)
i initially tried this:
let columns = [
GridItem(.adaptive(minimum: 42), alignment: .leading)
]
alongside
LazyVGrid(columns: columns, spacing: 10) {
Text("Hello, world!")
.fixedSize(horizontal: true, vertical: false)
.border(Color.gray)
Text("lol")
.fixedSize(horizontal: true, vertical: false)
.border(Color.gray)
Text("bananananananananana")
.fixedSize(horizontal: true, vertical: false)
.border(Color.gray)
}
but that didn't work. setting an explicitly large maximum (50000) didn't help either. my understanding here was that the LazyVGrid
should allow its children to expand their grid cells, but that's clearly not what's happening here.
i wanted to make sure this wasn't Text
being all shy and meek and declining to express its size preference, so i tried being more explicit with a frame:
LazyVGrid(columns: columns, spacing: 10) {
Text("sup")
.border(Color.gray)
Text("sup")
.border(Color.gray)
Text("sup")
.frame(minWidth: 100, maxWidth: 100)
.border(Color.gray)
Text("sup")
.border(Color.gray)
Text("sup")
.border(Color.gray)
Text("sup")
.border(Color.gray)
Text("sup")
.border(Color.gray)
Text("sup")
.border(Color.gray)
Text("sup")
.border(Color.gray)
Text("sup")
.border(Color.gray)
}
but this also doesn't work. i've gotten the wrapping behavior, but not the "flex" behavior, which at least by my reading is what .adaptive
purports to do on the tin.
so my secondary question is: what am i misunderstanding about the way that an adaptive LazyVGrid
performs size proposal to its children and negotiates the size of its "adaptive columns"? is there a way to do what i want with grids, and if not, why not (and what's a better plan)?