Reputation: 101
I would like to have a LazyVGrid of rows with same heights expanding/shrinking to fill the available parent height
Is it possible?
let columns = Array(repeating: GridItem(.flexible(minimum: 50, maximum: 100)), count: 3)
LazyVGrid(columns: columns, alignment: .leading, spacing: 10) {
ForEach(objects, id: \.self.id) { object in
MyView().frame(minHeight: 0, maxHeight: .infinity)
}
}
The above code does perfectly spaces the frames in width, but the rows don't expand and shrink, they just get tight to the height of the content.
Upvotes: 8
Views: 8053
Reputation: 5125
Here is an example of grid that takes all the height available. It is possible to do it in more generic way but I hope the idea of how to do it is clear.
struct ContentView: View {
let numOfItems = 10
let numOfColumns = 3
let spacing: CGFloat = 10
var body: some View {
GeometryReader { g in
let columns = Array(repeating: GridItem(.flexible(minimum: 50, maximum: 100)), count: numOfColumns)
let numOfRows: Int = Int(ceil(Double(numOfItems) / Double(numOfColumns)))
let height: CGFloat = (g.size.height - (spacing * CGFloat(numOfRows - 1))) / CGFloat(numOfRows)
LazyVGrid(columns: columns, alignment: .leading, spacing: spacing) {
ForEach(0..<numOfItems, id: \.self) { object in
MyView().frame(minHeight: height, maxHeight: .infinity)
}
}
}
}}
struct MyView: View {
var body: some View {
Color(red: Double.random(in: 0...1), green: Double.random(in: 0...1), blue: Double.random(in: 0...1))
}
}
And here goes a reusable version of such a grid:
struct ContentView: View {
let colors = [UIColor.red, .black, .blue, .brown, .gray, .green, .cyan, .magenta, .orange, .purple]
var body: some View {
TallVGrid(items: colors, idKeyPath: \.self, numOfColumns: 4, vSpacing: 20, content: { color in
ColorView(uiColor: color)
Text("Some \(Int.random(in: 0...10))")
})
}
}
struct TallVGrid<Item, ItemView, I>: View where ItemView: View, I: Hashable {
var items: [Item]
var idKeyPath: KeyPath<Item, I>
var numOfItems : Int {
items.count
}
var numOfColumns : Int = 3
var vSpacing: CGFloat = 10
@ViewBuilder var content: (Item) -> ItemView
var body: some View {
GeometryReader { g in
let columns = Array(repeating: GridItem(.flexible(minimum: 50, maximum: 100)), count: numOfColumns)
let numOfRows: Int = Int(ceil(Double(numOfItems) / Double(numOfColumns)))
let height: CGFloat = (g.size.height - (vSpacing * CGFloat(numOfRows - 1))) / CGFloat(numOfRows)
LazyVGrid(columns: columns, alignment: .leading, spacing: vSpacing) {
ForEach(items, id: idKeyPath) { item in
VStack {
content(item)
}
.frame(minHeight: height, maxHeight: .infinity)
}
}
}
}
}
struct ColorView: View {
let uiColor: UIColor
var body: some View {
Color(uiColor)
}
}
Upvotes: 6