Center HStack content excluding content in VStack

118 Views Asked by At

I have a pretty basic view with an HStack, that has an Image, VStack, Text, and another Image in it. What I would like for is the speedometer image, the ProgressView (which is in the VStack), the "75" text, and the chevron image to be centered horizontally. Maybe there is a better way to structure this, or some modifier I can apply that I'm not aware of. Again the goal is to have a horizontal row of content that is the Image, ProgressView, Text and an Image centered horizontally with the "Hello" text leading aligned to the ProgressView. Here is a screenshot of its current state and the code.

Default font

struct ContentView: View {
    var body: some View {
        HStack(alignment: .lastTextBaseline) {
            Image(systemName: "speedometer")
            VStack(alignment: .leading) {
                Text("Hello")
                ProgressView(value: 50, total: 100)
                    .progressViewStyle(.linear)
            }
            Text("75")
            Image(systemName: "chevron.forward")
        }
        .padding()
    }
}

This is what I want the alignment to look like. What is missing from this screenshot is I want the text "Hello" to be above the ProgressView, and leading aligned:

HStack

2

There are 2 best solutions below

1
Benzy Neez On BEST ANSWER

There would be a number of ways to achieve this layout. Techniques at your disposal include:

  • use .padding to shift items into position
  • use .offset to tweak positions
  • use fixed sizes for the items
  • use an .alignmentGuide
  • use overlays
  • use hidden content to reserve space
  • change the .baselineOffset for the Text
  • change the alignment of the stacks.

Here is a variation that uses a VStack containing two HStack. If the size of the speedometer image would be known then padding could be used to reserve this space. Since it is not known, a hidden version of the image is used instead:

VStack(alignment: .leading, spacing: 0) {
    HStack {
        Image(systemName: "speedometer")
            .hidden()
        Text("Hello")
    }
    HStack {
        Image(systemName: "speedometer")
        ProgressView(value: 50, total: 100)
            .progressViewStyle(.linear)
        Text("75")
        Image(systemName: "chevron.forward")
    }
}
.padding()

Screenshot

0
Roy Rodney On

What you want to do is a bit more complex than it appears at first - since you essentially want to insert an extra view (Text) into your aligned layout without affecting it.

Off of my head, you can try the following, progressively hacky solutions:

  1. Use a grid

  2. Bottom alignment for HStack, defining same height for the 3 aligned elements (you can define a view’s alignment in its own frame using something like: .frame(height: Foo, alignment: .center)

  3. Force the SwiftUI layout to ignore the Text by setting its height to 0

  4. GeometryReader