1 year ago

#341155

test-img

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()
    }
}

This is how my code looks on the simulator

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

Accepted video resources