Jacob F
Jacob F

Reputation: 147

How to make SwiftUI Buttons a fixed size?

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

Upvotes: 2

Views: 2702

Answers (4)

PAULMAX
PAULMAX

Reputation: 312

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

Upvotes: 1

vadian
vadian

Reputation: 285079

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

Upvotes: 1

Jeevan Thandi
Jeevan Thandi

Reputation: 168

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:

Upvotes: 4

BotBehavior
BotBehavior

Reputation: 11

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

Upvotes: -1

Related Questions