I am trying to create buttons which grow up to a maximum width if necessary at which point they use "..." to truncate their title. I know there are some unusual things about button frames, like the difference in applying frame before or after .buttonStyle
however this behavior is surprising to me.
It seems applying the maxWidth
makes the button take up that width even if it doesn't need it. If you apply a button style that gives it a background it would have whitespace around the button.
I have found this applies to Text()
as well.
struct TestButton: View {
let title: String
var body: some View {
Button {
print(title)
} label: {
Text(title)
.lineLimit(1)
}
.frame(maxWidth: 180)
.background(.purple)
.buttonStyle(PlainButtonStyle())
.focusable()
}
}
HStack {
TestButton(title: "Bears")
TestButton(title: "Battlestar Galactica")
}
.frame(maxWidth: 180)
is simply wrapping an invisible "frame" around the button. This frame is very flexible - if it receives a size proposal with a width of at most 180pt, it will choose its width to be the same as the proposed width. Otherwise, it will choose its width to be 180pt, effectively "capping" the proposal.
This is exactly what HStack
proposes to the frame
. HStack
will first try to propose an equal amount of width to each children, trying to lay them out evenly. The frame
s accept these proposals, because the HStack
happens to be less than 360pt wide*.
You can add fixedSize()
(or fixedSize(horizontal:vertical:)
) to each of the buttons, so that instead of proposing a specific width to the buttons, an .unspecified
proposal is sent to the frame
s. The frame
will forward this to the buttons and the buttons will choose a size that fits their content. Of course, if the width they chose is larger than 180pt, it will still be capped.
Adding fixedSize()
to the HStack
also works, because HStack
will also forward the .unspecified
proposal to its children, instead of trying to space the children out evenly (it literally cannot do this because there is no proposed width).
However, fixedSize()
will not allow the buttons to shrink either. If the HStack
is too narrow, the buttons will exceed the bounds of the HStack
. If this is undesirable, you might need to write your own Layout
. While it is relatively clear what you want to do in the case of 2 views, it's not very clear how you want this to work in general.
* More accurately, the HStack
itself received a size proposal with a width < 360pt + default spacing of the HStack
.