I’ve got a SwiftUI Button, and it’s label changes when it’s clicked.
The code looks like this:
struct MyButton: View {
var label: String
@State var loading = false
var body: some View {
Button(action: { loading.toggle() }) {
if (loading) {
Text("...")
} else {
Text(label)
}
}
.buttonStyle(.borderedProminent)
}
}
struct MyButton_Previews: PreviewProvider {
static var previews: some View {
MyButton(label: "This is a button")
}
}
When it’s rendered, the button changes size when it’s clicked and it’s label content changes, as you can see here:
How can I get the button to remain the same size no matter what it’s label content is?
I’m hoping to find a solution that doesn’t require hard coding a frame size or something like that, because I’m trying to create a generic, reusable button that can have any label.
By way of example of what I’m looking for, in Compose, I can use a custom layout for this. I can measure both the Text
view for the "loading" state and the Text
view for the "non-loading" state, and then set the parent/button size to the larger of the two, and then only place the Text
that should be visible, resulting in a button that’s big enough to show either label, and can switch between the two. Is there anything like that in SwiftUI?
3
Answers
To create a fixed-size SwiftUI Button with a changing label, use a VStack with conditional Text views and set a fixed size with the frame modifier. Adjust opacity based on the loading state.
You can use
overlay
And if you want to add a little animation you can use
TimelineView
.If you don’t want to hardcode the color and you can always "hide" the label with opacity.
If you want the button to adopt its natural size for the full label then you can use a hidden version of the label to establish the footprint and then show the relevant label as an overlay. This avoids the need to set a frame with any kind of minimum or fixed width.
If you don’t know which of the two labels is the longest then you can use a
ZStack
to establish the footprint instead: