2

I am trying to create 2 buttons of equal size, but the size is determined by the text inside the button. The two buttons are "login" and "create account", so the "create account" button is larger. I tried to use .frame() to adjust the size, but that just made it have extra padding around it.

Here is my code:

 HStack {
                    Button(action: {
                        print("Login button pressed")
                    }) {
                    Text("Login")
                        .padding()
                        .foregroundColor(Color.white)
                        .background(Color(UIColor.buttonColor))
                        .cornerRadius(15)
                    
                        
                    }
                    
                    Button(action: {
                        print("Create Account button pressed")
                    }) {
                    Text("Create Account")
                        .padding()
                        .foregroundColor(Color.white)
                        .background(Color(UIColor.buttonColor))
                        .cornerRadius(15)
                    }
                }

And here is what that displays

buttons

2
  • 1
    Does this answer your question stackoverflow.com/a/67454262/12299030? Of course there is simplest way - use same label for smaller button with transparent color and overlay of needed label. Commented Jan 13, 2022 at 19:47
  • HStack { Button { } label: { Text("Login") } .frame(width: 146.0, height: 36.0) .background(Color(uiColor: UIColor.buttonColor)) .cornerRadius(15.0) .foregroundColor(Color.white) Button { } label: { Text("Create Account") } .frame(width: 146.0, height: 36.0) .background(Color(uiColor: UIColor.buttonColor)) .cornerRadius(15.0.0) .foregroundColor(Color.white) } Commented Jan 14, 2022 at 0:16

4 Answers 4

4

Best to make a button style you can use across your app which will also ensure any future stack is proportionally filled. Otherwise you'll find your UIs will get messy making Buttons with Text()s like that.

Laying out the buttons with a custom Style in the HStack:

 HStack(spacing: 20) {
     Button("Login", action: { print("login")})
            .buttonStyle(PrimaryButtonStyle())
     Button("Create Account", action: { print("create account")})
            .buttonStyle(PrimaryButtonStyle())
    }.padding([.leading, .trailing], 10)

PrimaryButtonStyle:

struct PrimaryButtonStyle: ButtonStyle {

var backgroundColor: Color = .black
var textColor: Color = Color.white
var height: CGFloat = 46
var cornerRadius: CGFloat = 15
var fontSize: CGFloat = 15
var disabled: Bool = false
var textSidePadding: CGFloat = 30
var weight: Font.Weight = .semibold

func makeBody(configuration: Configuration) -> some View {
    configuration.label
        .padding([.leading, .trailing], textSidePadding)
        .frame(maxWidth: .infinity, maxHeight: height)
        .background(disabled ? .gray : backgroundColor)
        .foregroundColor(textColor)
        .cornerRadius(cornerRadius)
        .font(.system(size: fontSize, weight: weight, design: .default))
        .scaleEffect(configuration.isPressed ? 1.2 : 1)
        .animation(.easeOut(duration: 0.2), value: configuration.isPressed)
    }
}

Result:

enter image description here:

Sign up to request clarification or add additional context in comments.

Comments

1

In iOS 16, macOS 13 Apple introduced the Layout protocol.

This code is from the WWDC 2022 video Compose custom layouts with SwiftUI, it resizes multiple buttons horizontally to an equal width.

struct ButtonEqualWidth: Layout {
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let maxSize = maxSize(subviews: subviews)
        let spacing = spacing(subviews: subviews)
        let totalSpacing = spacing.reduce(0, +)
        return CGSize(width: maxSize.width * CGFloat(subviews.count) + totalSpacing,
                      height: maxSize.height)
    }
    
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        let maxSize = maxSize(subviews: subviews)
        let spacing = spacing(subviews: subviews)
        let sizeProposal = ProposedViewSize(width: maxSize.width, height: maxSize.height)
        var x = bounds.minX + maxSize.width / 2
        for index in subviews.indices {
            subviews[index].place(
                at: CGPoint(x: x, y: bounds.midY),
                anchor: .center,
                proposal: sizeProposal)
            x += maxSize.width + spacing[index]
        }
    }
    
    func maxSize(subviews: Subviews) -> CGSize {
        let subviewSizes = subviews.map{ $0.sizeThatFits(.unspecified) }
        return subviewSizes.reduce(.zero) { currentMax, subviewSize in
            CGSize(
                width: max(currentMax.width, subviewSize.width),
                height: max(currentMax.height, subviewSize.height)
            )
        }
    }
    
    func spacing(subviews: Subviews) -> [Double] {
        return subviews.indices.map { index in
            guard index < subviews.count - 1 else { return 0.0 }
            return subviews[index].spacing.distance(
                to: subviews[index + 1].spacing,
                along: .horizontal)
        }
    }
}

In your example add this modifier to both Text views

.frame(maxWidth: .infinity) 

and replace HStack with ButtonEqualWidth

Comments

1

How to make two views the same width or height

This approach works just as well when you want to make two views have the same width:

VStack {
    Button("Log in") { }
        .foregroundColor(.white)
        .padding()
        .frame(maxWidth: .infinity)
        .background(.red)
        .clipShape(Capsule())

    Button("Reset Password") { }
        .foregroundColor(.white)
        .padding()
        .frame(maxWidth: .infinity)
        .background(.red)
       .clipShape(Capsule())
}
.fixedSize(horizontal: true, vertical: false)

enter image description here

with ↑ .fixedSize(horizontal: true, vertical: false) & without it

it is important to understand that .frame(maxWidth: .infinity) also matters

And for HStack {} ... of course))) sorry...

.fixedSize(horizontal: false, vertical: true)

enter image description here

But there may be more text and the result may be unexpected: enter image description here

... in that case we use the code:

(add to all buttons, because we don't know how much text will be by text localizing):

.frame(maxHeight: .infinity)

enter image description here

1 Comment

Be aware that fixedSize can overflow its parent, from the docs: This can result in the view exceeding the parent’s bounds, which may or may not be the effect you want.
-1

.frame and set width to UI screen - blank should work

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.