skip to Main Content

A similar question has been answered for Android Compose in this post. I was wondering if the same can be done for SwiftUI?

Example:

HStack {
    VStack {
        ContentWithUnknownHeight()
        NextContentWithUnknownHeight()
    }
    .frame(width: geometry.size.width * 0.8)
    Image("User")
        .resizable()
        .frame(width: geometry.size.width * 0.2)
}
.frame(minHeight: 100)

HStack’s height should not be fixed but grow if its childrens do. If VStack is taller than the Image, how can I fill the Image’s height to its parent’s without influencing its height?

What I have:

What I have

Expected Result:

Expected

2

Answers


  1. An HStack automatically adopts the height of the tallest child.

    However, your question seems more concerned about the height of a child inside the HStack and your code example has an image in this position. Assuming you actually want to scale the image to fill (as opposed to scaling to fit, which is how the code is implemented), then one way is to show the image in an overlay over the HStack:

    • Remove the .frame modifier that is setting a fixed height on the HStack.
    • Replace the Image inside the HStack with a Spacer.
    • The height of the HStack will then be determined by the first child.
    • Apply the overlay with alignment: .trailing.
    • An overlay automatically adopts the frame of the view it is applied to, so it’s height will also be the height of the first child.
    • Scale the image to fill and then clip to the bounds of the frame.
    HStack {
        VStack {
            SomeMoreContentWithUnknownHeight()
        }
        .frame(width: geometry.size.width * 0.8)
        Spacer()
    }
    .overlay(alignment: .trailing) {
        Image("User")
            .resizable()
            .scaledToFill()
            .frame(width: geometry.size.width * 0.2)
            .clipped()
    }
    

    Screenshot

    If SomeMoreContentWithUnknownHeight is the only content left in the HStack then you don’t even need to use an HStack, you can apply the overlay directly to this content instead. Just use padding to reserve space for the overlay content:

    SomeMoreContentWithUnknownHeight()
        .frame(maxWidth: .infinity)
        .padding(.trailing, geometry.size.width * 0.2)
        .overlay(alignment: .trailing) {
            Image("User")
                .resizable()
                .scaledToFill()
                .frame(width: geometry.size.width * 0.2)
                .clipped()
        }
    
    Login or Signup to reply.
  2. To make the HStack doesn’t grow taller than its tallest child needs, add

    .fixedSize(horizontal: false, vertical: true)
    

    to the HStack.

    Then, if the other views are views that naturally expand (e.g. Divider, Rectangle, Image(...).resizable()), they will expand to fill the height automatically. For an image, you might want it to be scaledToFill, like in Benzy Neez’s answer.

    For views that do not naturally expand, like Text, you can make them do that by putting them as an overlay of some view that does naturally expand, plus maxHeight: .infinity.

    Color.clear // Colors naturally expand
        .overlay {
            Text("Foo")
                .frame(maxHeight: .infinity)
                .background(.yellow) // this background will fill the whole height
        }
    

    Some views like Buttons may need special handling, to expand their tappable area, not just their frame. See also SwiftUI: Increase tap/drag area for user interaction

    Button("Foo") { /* ... */ }
        .frame(maxHeight: .infinity)
        .background(.yellow)
        .contentShape(Rectangle())
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search