So I just need the views within an HSTack
to each take the entire width available to them so the space is evenly distributed and all have the same width.
As for height, they should also all have the same height regardless of text length.
Current code:
struct TestViewHStack: View {
let strings = ["Short", "some long text", "short"]
var body: some View {
HStack {
ForEach(strings, id: \.self) { title in
CardView(title: title)
.frame(maxWidth: .infinity)
}
}
}
}
struct CardView: View {
let title: String
var body: some View {
VStack(spacing: 8) {
Image(systemName: "star.fill")
.resizable()
.frame(width: 20, height: 20)
Text(title)
.font(.subheadline)
}
.padding(16)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: .gray.opacity(0.2), radius: 8, x: 0, y: 2)
}
}
Output:
I thought setting .frame(maxWidth: .infinity)
would make it each take the entire width available, but it doesnt seem to do that. Also tried messing with frames and fixedSize()
modifiers but wasnt able to achieve the desired result.
Thanks!
They already have the same width, but you cant see it because you apply the height after setting the background. So reorder the modifiers to make it visible:
struct TestViewHStack: View {
let strings = ["Short", "some long text", "short"]
var body: some View {
HStack {
ForEach(strings, id: \.self) { title in
CardView(title: title)
// .frame(maxWidth: .infinity) // 👈 This is after the background framing
}
}
}
}
struct CardView: View {
let title: String
var body: some View {
VStack(spacing: 8) {
Image(systemName: "star.fill")
.resizable()
.frame(width: 20, height: 20)
Text(title)
.font(.subheadline)
}
.padding(16)
.frame(maxWidth: .infinity) // 👈 This does the job as expected
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: .gray.opacity(0.2), radius: 8, x: 0, y: 2)
}
}
If you just need a fixed height, just add a maxHeight
to the frame and control the height of the stack.
But it seems like you don't know the height and you need all items to have the same size with the tallest one. So you need to let them size themselves and update anyone with the shorter height. This is where onPreferenceChange
comes in and requires some work.
struct TestViewHStack: View {
let strings = ["Short", "some long text", "short"]
@State private var maxCardHeight: CGFloat = 10
var body: some View {
HStack {
ForEach(strings, id: \.self) { title in
CardView(title: title)
.fixedSize(horizontal: false, vertical: true)
.background {
GeometryReader { proxy in
Color.clear.preference(
key: MaxValue.self,
value: proxy.size.height
)
}
}
.onPreferenceChange(MaxValue.self) {
maxCardHeight = $0
}
.frame(maxWidth: .infinity, maxHeight: maxCardHeight)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(color: .gray.opacity(0.2), radius: 8, x: 0, y: 2)
}
}
}
}
public enum MaxValue: PreferenceKey {
public static let defaultValue = CGFloat()
public static func reduce(value: inout Value, nextValue: () -> Value) {
print("value: \(value), next: \(nextValue())")
value = max(value, nextValue())
}
}
struct CardView: View {
let title: String
var body: some View {
VStack(spacing: 8) {
Image(systemName: "star.fill")
.resizable()
.frame(width: 20, height: 20)
Text(title)
.font(.subheadline)
}
.padding(16)
}
}
Note that you must first size and then apply the background. So You need to rearrange your UI logic
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With