HStack width changed when button label is changed

33 views Asked by At

I created a HStack to include 3 buttons for TTS speaking control. However, when the middle button state is changed between play and pause. As the width of these 2 buttons is different I think, then the HStack width is changed accordingly.

How could I prevent this behavior to have a consistent width of HStack in this case?

enter image description here

struct HomeTest: View {
    @State private var isSpeaking: Bool = false
    
    var body: some View {
        HStack(spacing: 18) {
            Button {
                
            } label: {
                Image(systemName: "gobackward")
                    .font(.system(size: 32))
                    .padding(6)
                    .foregroundStyle(.darkBlue10)
                    .clipShape(Circle())
            }
            Button {
                isSpeaking.toggle()
            } label: {
                Image(systemName: (isSpeaking ? "pause" : "play"))
                    .font(.system(size: 32))
                    .padding(6)
                    .foregroundStyle(.darkBlue10)
                    .clipShape(Circle())
            }
            Button {
                
            } label: {
                Image(systemName: "goforward")
                    .font(.system(size: 32))
                    .padding(6)
                    .foregroundStyle(.darkBlue10)
                    .clipShape(Circle())
            }
        }
        .padding(.horizontal, 6)
        .padding(.vertical, 4)
        .background(.lightBlue10.opacity(0.7))
        .mask { Capsule() }
        .overlay {
            RoundedRectangle(cornerRadius: 36)
                .inset(by: 1)
                .stroke(.white, lineWidth: 1)
        }
    }
}
2

There are 2 answers

2
son On BEST ANSWER

You can provide a minimum width for this Image:

Button {
    isSpeaking.toggle()
} label: {
    Image(systemName: (isSpeaking ? "pause" : "play"))
        .frame(minWidth: 30) //<- set minWidth here
        .font(.system(size: 32))
        .padding(6)
        .foregroundStyle(.darkBlue10)
        .clipShape(Circle())
}

Output:

enter image description here

0
Mahi Al Jawad On

The reason you are facing the problem: The Image for 'play' and 'pause' has different width. So when you change the button image by tapping the button then its outer wrapper view's size (width) also changes to fit the changed width Image of play/pause.

Fix: The conceptual fix for such a problem is: for an updatable view (In this case your play/pause Image) you should always use a fixed size to make the UI look consistent with different sized views.

For you code try this:


            Button {
                isSpeaking.toggle()
            } label: {
                Image(systemName: (isSpeaking ? "pause" : "play"))
                    .frame(width: 30, height: 30) // Follow your UX guide specification
                    .font(.system(size: 32))
                    .padding(6)
                    .foregroundStyle(.darkBlue10)
                    .clipShape(Circle())
            }