Reputation: 5299
I have submitted a bug to Apple.
I have a dashboard-style screen in my SwiftUI app, where I am using a LazyVGrid
with a single .adaptative
column to layout my dashboard widgets, where widgets are laid out in wrapping rows.
It works as I want it to.
However, if a widget happens to be taller than others, I would like other widgets in the same row to grow vertically, so they end up having the same height as the tallest of the row.
This small bit of code illustrates my problem:
struct ContentView: View {
var body: some View {
LazyVGrid(columns: [.init(.adaptive(minimum: 45, maximum: 50), alignment: .top)]) {
VStack {
Spacer()
Text("Hello")
}
.border(.red)
Text("Lorem ipsum")
.border(.blue)
}
.border(.green)
.padding(.horizontal, 100)
}
}
The result is:
I would like the red box (VStack
containing Spacer
+ Hello) to be as tall as the blue box (lorem ipsum).
How could I accomplish that?
Please don't suggest using an HStack
, as the above example is only to illustrate my problem with LazyVGrid
. I do need to use the grid because I have quite a few children to layout, and the grid works great between phone and iPad form factors (adjusting the number of columns dynamically, exactly as I want it).
Upvotes: 22
Views: 10353
Reputation: 61
If Apple is using fixedSize
to calculate the height of the row then putting .frame(maxHeight: .infinity)
on each cell will fill the pre-calculated height.
Tried it on iOS 17 and it works.
Upvotes: 6
Reputation: 89
I had the same issue. What worked for me was to set the minHeight of Spacer to 0.
here is some dummy example code
struct MasterView: View {
let columns = [
GridItem(.flexible(), spacing: 12),
GridItem(.flexible())
]
var body: some View {
VStack {
LazyVGrid(columns: columns, spacing: 12) {
ForEach(infoTexts, id:\.self) { infoText in
InfoFieldView(text: infoText)
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
struct InfoFieldView: View {
var text: String
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text("Some text")
.padding(.bottom, 4)
// Autoresizing does not work if you dont set minLenght
Spacer(minLength: 0)
}
}
}
Upvotes: 0
Reputation: 257711
It looks like Apple begins (for unknown reason) to apply fixedSize
for views in grid to make layout based on known intrinsic content sizes. The Spacer, Shape, Color, etc. do not have intrinsic size so we observe... that what's observed.
A possible approach to resolve this is perform calculations by ourselves (to find dynamically max height and apply it to all cells/views).
Here is a demo (with simple helper wrapper for cell). Tested with Xcode 13.2 / iOS 15.2
struct ContentView: View {
@State private var viewHeight = CGFloat.zero
var body: some View {
LazyVGrid(columns: [.init(.adaptive(minimum: 45, maximum: 50), alignment: .top)]) {
GridCell(height: $viewHeight) {
VStack {
Spacer()
Text("Hello")
}
}.border(.red)
GridCell(height: $viewHeight) {
Text("Lorem ipsum asdfd")
}.border(.blue)
}
.onPreferenceChange(ViewHeightKey.self) {
self.viewHeight = max($0, viewHeight)
}
.border(.green)
.padding(.horizontal, 100)
}
}
struct GridCell<Content: View>: View {
@Binding var height: CGFloat
@ViewBuilder let content: () -> Content
var body: some View {
content()
.frame(minHeight: height)
.background(GeometryReader {
Color.clear.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height)
})
}
}
struct ViewHeightKey: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value += nextValue()
}
}
Upvotes: 5
Reputation: 13
I had exactly same problem. My LazyVGrid looked great on iOS 14, but now its items have different heights.
I found a dirty workaround to force the items have same height: In my case I have only several items in each LazyVGrid (So it won't cause too much performance drop), and it is easy for me to know which item has the largest height. So I made a ZStack and put a transparent highest item behind the actual item.
struct ContentView: View {
var body: some View {
LazyVGrid(columns: [.init(.adaptive(minimum: 45, maximum: 50), alignment: .top)]) {
ZStack {
Text("Lorem ipsum") // This is the highest item.
.opacity(0) // Make it transparent.
Text("Hello")
}
.border(.red)
Text("Lorem ipsum")
.border(.blue)
}
.border(.green)
.padding(.horizontal, 100)
}
}
This workaround works in my case, but I don't recommend using it widely in your app especially when you have a lot of items in the grid.
Upvotes: 0