Reputation: 147
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
Upvotes: 2
Views: 2702
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)
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)
But there may be more text and the result may be unexpected:
... 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)
Upvotes: 1
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
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:
Upvotes: 4