1 year ago
#341155
Holger
SwiftUI Geomteryreader in ForEach does not set height of child dynamically
First off, I'm trying to learn SwiftUI so I may not have a full understanding of how it works.
I have an array of sentences that I use in a ForEach loop, that creates a GeometryReader with a Text view for each sentence.
The length of each sentence might not be the same, therefore the Text view may have multiple lines so the entire sentence fits. This means I need each GeometryReader to set its height according to the height of its child view (the Text view) so they don't overlap and get as much space as they need.
I found someone here on stackoverflow with the same problem and tried to do the same in my code. Unfortunately, it gets only the first GeometryReader's height and uses it on all of them. So those GeometryReader's that should have a smaller height than the first, are getting the same amount.
It should be said the reason I use GeometryReader, is to get each Text view to change based on their location on screen. I have not reached that point in my development yet, though, but I'm going to use it later. Right now I'm stuck at setting each GeometryReader's height as they need to be.
Here's my code and how it looks on Preview:
struct ContentView: View {
@State var sentences: [String] = ["Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "Lorem ipsum dolor", "Praesent in ligula in libero ullamcorper finibus.", "Etiam tortor orci", "Morbi porttitor vehicula tincidunt."]
@State private var fitHeight = CGFloat(0)
var body: some View {
ScrollView{
ForEach(sentences, id: \.self) { sentence in
GeometryReader { geo in
Text(sentence)
.font(.title.bold())
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.leading)
.background(
GeometryReader { proxy in
Color.clear.preference(key: ViewHeightKey.self, value: proxy.frame(in: .local).size.height)
}
)
.padding()
}
.onPreferenceChange(ViewHeightKey.self) { self.fitHeight = $0 }
.frame(height: fitHeight)
}
}
}
}
struct ViewHeightKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
As you can see, the first GeometryReader sets its own height at what its Text views height is. Great!
Then the next GeometryReader's Text view is only one line high so it does not need to be as high as the first, but for some reason, it sets it height to the same as the first GeometryReader. Notice the white space between the "Lorem ipsum dolor" Text view and the "Praesent in ligula in libero ullamcorper finibus". That happens because the second GeometryReader's height is more than it needs.
It's almost like fitHeight does not get updated between the creation of each GeometryReader.
Try to change the Color.clear to something like Color.gray to see how big the Text views render themselves.
How do I make it so each GeometryReader sets its own height based on the height of its child view?
Thanks in advance!
swiftui
geometryreader
0 Answers
Your Answer