2

I would like to get the frame of a view in a LazyVStack. The LazyVStack embedded in a ScrollView displays chat messages (Text and Images).

Because the content is dynamically sized I am not able to use a GeometryReader Proxy. Putting a GeometryReader in my LazyVStack breaks the layout. ( I assume because of the dynamic sizing. ).

Is there any other way to get the frame of the views in the LazyVStack?

Below is a simple representation of what I want to do. Ideally I would like to have a GeometryReader in the MessageView but that messes up the vertical spacing.

struct ChatView: View {
    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                LazyVStack {
                    ForEach(messages, id: \.id) { message in
                        MessageView(message: message)
                    }
                }
            }
        }
    }
}

struct MessageView: View {
    
    var message: Message
    
    var body: some View {
        // GeometryReader here is not possible with dynamic row heights
        if message.text != nil {
            Text(message.text!)
        } else if message.imageData != nil {
            Image(uiImage: UIImage(data: message.imageData!))  // <-- Get frame of this Image
        }
    }
}

Combining the MessageView into the ChatView is not desirable because the MessageView is quite involved with many views and needs to be an independent struct.

alionthego
  • 8,508
  • 9
  • 52
  • 125

1 Answers1

3

You need to use GeometryReader in background of view which frame you want to measure, like

var body: some View {
    if message.text != nil {
        Text(message.text!)
    } else if message.imageData != nil {
        Image(uiImage: UIImage(data: message.imageData!))
          .background(GeometryReader { gp in
             // gp.frame(in: .global)   // << eg. frame in global coordinates
          })
    }
}

See also https://stackoverflow.com/a/62466397/12299030

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 1
    I'm trying to use this frame but I'm getting Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols when I try to save or print the frame in the closure. This is promising but how can I assign that frame from the closure when say I tap on the Image – alionthego Apr 24 '21 at 11:29
  • I ended up putting a Color.clear view in the background block and attaching an .onReceive to it which receives the button push and saves the frame to the viewModel. Thanks. – alionthego Apr 24 '21 at 12:24