'How to make a view between Spacers always at the center in an HStack?

What I would like to achieve. regardless the width of text width at both side the button should always at the center of the HStack.

enter image description here

HStack {
    Text("Foooooooo")
    Spacer(minLength: 5)
    Button(action: { }) {
        Text("Bar")
    }
    Spacer()
    Text("Baz")
}
.font(.system(size: 16, weight: .heavy, design: .rounded))
.padding()

enter image description here

I also tried to use GeometryReader and set frame size for each Text and Button in the view however there are two problems,

  1. The view returned by GeometryReader would occupies the entire view the parent offers to it instead of the actual intrinsic content size, the space only enough for Text, Spacer and Button
  2. String inside the first Text could not be left align so does the string inside the last Text couldn't be right aligned


Solution 1:[1]

Here is possible approach for your case. Demo prepared & tested with Xcode 12 / iOS 14

demo

    HStack {
        Spacer()
            .overlay(Text("Foooooooo"), alignment: .leading)

        Button(action: { }) {
            Text("Bar")
        }

        Spacer()
            .overlay(Text("Baz"), alignment: .trailing)
    }
    .font(.system(size: 16, weight: .heavy, design: .rounded))
    .padding()

backup

Solution 2:[2]

You want to layout three flexible-but-same-sized elements, but you have three fixed-but-differently-sized elements. To fix that, put each element in its own flexible stack (HStack w/ spacer) so that each get 1/3 of the space. Within each stack, use Spacers to align.

    HStack {
        // Left stack
        HStack {
            Text("Foooooooo")
            Spacer()
        }

        // Center stack. The surrounding Spacers aren't really required in this
        // specific case, but added for consistency and to show how to center.
        HStack {
            Spacer()
            Button(action: { }) {
                Text("Bar")
            }
            Spacer()
        }

        // Right stack
        HStack {
            Spacer()
            Text("Baz")
        }
    }
    .font(.system(size: 16, weight: .heavy, design: .rounded))
    .padding()

Solution 3:[3]

Here is another way to do it which is pretty concise and uses a ZStack to combine the centered button with an HStack with a single Spacer to push the labels to the edges:

ZStack {
    HStack {
        Text("Foooooooo")
        Spacer()
        Text("Baz")
    }
    Button(action: { }) {
        Text("Bar")
    }
}
.font(.system(size: 16, weight: .heavy, design: .rounded))
.padding()

Note: This solution works if you know your labels won't encroach on your button. @Asperi's solution causes extra long labels to be truncated with .... @RobNapier's solution causes extra long labels to wrap at 1/3 of the width.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Rob Napier
Solution 3