8

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.

Expected

2
  • Did you solve this? I'm interested in dynamic row height for LazyVGrid. Commented Jul 16, 2021 at 11:19
  • nop, never found a proper dynamic solution. I've solved it for my case by hardcoding a fixed height to the whole LazyVGrid. I originally wanted it to expand to the size of the parent Window (working on a os x app) Commented Jul 22, 2021 at 18:00

1 Answer 1

6

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)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.