Steffen
Steffen

Reputation: 169

Make a View the same size as another View which has a dynamic size in SwiftUI

I want to achieve this (For the screenshots I used constant heights, just to visualize what I am looking for):

Goal (1) Goal (1)

The two Views containing text should together have the same height as the (blue) Image View. The right and left side should also be of the same width. The important part: The Image View can have different aspect ratios, so I can't set a fixed height for the parent View. I tried to use a GeometryReader, but without success, because when I use geometry.size.height as height, it takes up the whole screen height.

This is my code:

import SwiftUI

struct TestScreen: View {
    var body: some View {
        VStack {
            GeometryReader { geometry in
                HStack {
                    Image("blue-test-image")
                        .resizable()
                        .scaledToFit()
                        .frame(maxWidth: .infinity)
                    VStack {
                        Text("Some Text")
                            .padding()
                            .frame(maxWidth: .infinity, maxHeight: .infinity)
                            .background(Color.gray)
                        Text("Other Text")
                            .padding()
                            .frame(maxWidth: .infinity, maxHeight: .infinity)
                            .background(Color.gray)
                    }
                    .frame(maxWidth: .infinity, maxHeight: geometry.size.height /* should instead be the height of the Image View */)
                }
            }
            Spacer()
        }
        .padding()
    }
}

This is a screenshot of what my code leads to: enter image description here

Any help would be appreciated, thanks!

Upvotes: 0

Views: 3834

Answers (2)

Spencer Shelton
Spencer Shelton

Reputation: 391

To go further on @Steffen answer I moved the logic into a view extension.

extension View {

func getHeightOfView( completion: @escaping (_ height: Double) -> Void) -> some View {
    self.background {
        GeometryReader {
            Color.clear.preference(
                key: ViewHeightKeyTestScreen.self,
                value: $0.frame(in: .local).size.height
            )
        }
    }.onPreferenceChange(ViewHeightKeyTestScreen.self) {
        completion($0)
    }
}}

Now you can just add ".getHeightOfView" to a view you are trying to get the height of.

struct TestView: View {
@State var heightOfElement: CGFloat = CGFloat.zero
var body: some View {
    HStack{
        Text("short text this view will be the height of the other view")
            .padding(10)
          
            .frame(height: heightOfElement)
            .frame(maxWidth: .infinity)
            .background(Color.red)
           
        Text("long long long text, this text is where we get height of other view, this is the tallest height element in hstack")
            .padding(10)
            .frame(maxWidth: .infinity)
            .background(Color.red)
            .getHeightOfView { height in
                self.heightOfElement = height
            }
    }
}}

Upvotes: 0

Steffen
Steffen

Reputation: 169

This is how I solved it in this case:

import SwiftUI

struct TestScreen: View {
    @State private var imageHeight = CGFloat.zero

    var body: some View {
        VStack {
            HStack {
                Image("blue-test-image")
                    .resizable()
                    .scaledToFit()
                    .frame(maxWidth: .infinity)
                    .background(GeometryReader {
                        Color.clear.preference(
                            key: ViewHeightKeyTestScreen.self,
                            value: $0.frame(in: .local).size.height
                        )
                    })
                VStack {
                    Text("Some Text")
                        .padding()
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .background(Color.gray)
                    Text("Other Text")
                        .padding()
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .background(Color.gray)
                }
                .frame(maxWidth: .infinity, minHeight: self.imageHeight, maxHeight: self.imageHeight)
            }
            .onPreferenceChange(ViewHeightKeyTestScreen.self) {
                self.imageHeight = $0
            }
            Spacer()
        }
        .padding()
    }
}

struct ViewHeightKeyTestScreen: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

struct TestScreen_Previews: PreviewProvider {
    static var previews: some View {
        TestScreen()
    }
}

Upvotes: 3

Related Questions