1

I want to be able to show an array of notes in a ScrollView. Each of these objects contains a GeometryReader, and I'm having trouble setting each of these object's height by the amount of content that is in it.

I've made a reproducible example below (I know in this example it doesn't make sense why I need the geometry reader but I do need it in my actual app).

import SwiftUI

struct ContentView: View {
    var body: some View {
        ScrollView {
            ForEach(notes) { note in
                NoteView(note: note)
            }
        }
    }
}

struct NoteView: View {
    var note: Note = notes[0]
    
    var body: some View{
        GeometryReader { geo in
            Text(note.content)
                .fixedSize(horizontal: false, vertical: true)
                .frame(width: geo.size.width)
        }
    }
}


struct Note: Identifiable {
    var id = UUID()
    var content: String = ""
}

var notes = [
    Note(content: "Lorem ipsum dolor sit amet, consectet adipiscing elit. Condimentum quisque id vitae convallis dignissim pharetra nisl est creatus"),
    Note(content: "Mauris ac tempor libero, non eleifend lectus. Mauris eu hendrerit nunc. Donec at ante mauris. Duis ac elit purus. Mauris ullamcorper mi."),
    Note(content: "Lorem ipsum dolor sit amet, consectet adipiscing elit. Condimentum quisque id vitae convallis dignissim pharetra nisl est creatus"),
    Note(content: "Mauris ac tempor libero, non eleifend lectus. Mauris eu hendrerit nunc. Donec at ante mauris. Duis ac elit purus. Mauris ullamcorper mi.")
]

When I use embed the ForEach loop in a ScrollView all of the items overlap: Items overlapping when using ScrollView

But if I change the ScrollView to a VStack, I get more what I am looking for with the items appearing stacked on top of each other.

VStack allows the correct height of the object

I believe that overlapping is because the height of the GeometryReader is less than the height of the Text within the GeometryReader.

If I manually add .frame(height: 100) to the GeometryReader, the objects no longer overlap. But I do not know how I could create a variable for the height that would be based on the amount of Text each note contains.

Hal219
  • 61
  • 5
  • Why would you want to set the height manually? – cora Jul 01 '22 at 16:52
  • I believe the text is overlapping because the height of the GeometryReader is far less than the height of the text within the geometry reader. Im making this assumption because I tried adding .frame(height: 100) to the GeometryReader, and the objects no longer overlap. I just don't know how I could create a variable that would set this height based on the amount of content in each Note. – Hal219 Jul 01 '22 at 16:57

4 Answers4

2

Try with frame() Example :

Text(myText).frame(height: 100)

Hope it will work.

Edit: i just suggested you to use frame() Height or width fully depends on you. You may use your dynamic value by replacing height:100 Hope this make sense. Thanks

Al Mustakim
  • 480
  • 4
  • 11
0

You don't need GeometryReader for that, it can be done just with .frame capabilities, like

struct NoteView: View {
    var note: Note = notes[0]
    
    var body: some View{
      Text(note.content)
//        .fixedSize(horizontal: false, vertical: true) // this might be not needed as well
        .frame(maxWidth: .infinity)     // << here !!
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • I do need the GeometryReader in my actual app because each NoteView contains a custom drag gesture. – Hal219 Jul 01 '22 at 17:01
  • Anyway GeometryReader at that level will not work in ScrollView, by nature of both. ... and, for future, if you want to get right answer you have to provide right code in question - details are important. – Asperi Jul 01 '22 at 17:05
0

It seems all texts are shown on screen but problem occurs because of GeometryReader.

I have a solution for you. If all texts are shown on screen with their all height we can get that height with adding a background modifier to all of them. And inside that background we can add another GeometryReader to get that background height.

struct NoteView: View {
    let note: Note
    @State private var height: CGFloat = 0
    
    var body: some View{
        GeometryReader { geo in
            Text(note.content)
                .fixedSize(horizontal: false, vertical: true)
                .background {
                    GeometryReader { proxy in
                        Text("")
                            .onAppear {
                                self.height = proxy.size.height
                            }
                    }
                }
        }
        .frame(height: self.height)
    }
}
Gorkem Sevim
  • 384
  • 5
  • 9
0

I recently ran into a similar issue with GeometryReader and I ended up finding what I think is probably a better solution. Instead of using GeometryReader I used UIScreen.main.bounds.

For example:

You could create an extension of View to retrieve the bounds.

extension View {
    func viewBounds() -> CGRect {
      return UIScreen.main.bounds
    }
}

Having this value you are then able to pass it into your .frame() modifier like this

struct NoteView: View {
    var note: Note = notes[0]
    
    var body: some View{
            Text(note.content)
                .fixedSize(horizontal: false, vertical: true)
                .frame(width: viewBounds().width)
    }
}

I think most likely the GeometryReader doesn't work well with ScrollView because it is meant to take up all the space in a view & since a ScrollView is ever growing it leads to overlapping and just a buggy view in general.

Hope this helps!