Published on

Finally found a use case for .fixedSize šŸ˜…

Authors
  • avatar
    Name
    Omar Elsayed
    Twitter

Introduction

If you told me a week ago that .fixedSize would save my afternoon, I would have laughed. The name alone screams "this will paint you into a corner"—who actually wants their views locked to a static size? So like a lot of SwiftUI devs, I quietly filed it under "modifiers I'll never reach for."

Then a layout requirement landed on my desk that, surprise surprise 😳, only .fixedSize could solve cleanly. And it taught me a lesson I should have learned years ago: there are no bad APIs, only APIs you haven't found the use case for yet. SwiftUI ships every modifier for a reason. Avoiding one on principle just shrinks your toolbox for no good reason.

Let me walk you through the problem.

The challenge

The brief was simple:

"We want all the items in the scroll view to take the same height as the biggest one."

Picture a horizontal ScrollView with an HStack inside. Each item is a card with a title and a description. The descriptions vary in length—some short, some long—and the design team didn't want to truncate them. They just wanted every card to match the height of the tallest one so the row looks clean and intentional instead of jagged.

The problem

In the image above, the left card is shorter than the right one. The goal: make the left card stretch to match the right card's height, no matter which item happens to be the tallest.

Sounds trivial, right? It isn't—especially when you've spent years avoiding the one modifier that would unlock it šŸ”“.

The first attempt (and why it backfired)

My first instinct was the obvious one: tell each card to fill all available vertical space.

ScrollView(.horizontal) {
    HStack(alignment: .top, spacing: 16) {
        ForEach(items) { item in
            CardView(item: item)
                .frame(maxHeight: .infinity, alignment: .topLeading)
        }
    }
}

The result?

The problem

Every card stretched to fill the entire screen height. Technically the cards were all the same height now—mission accomplished, I guess?—but the row was eating the whole viewport.

Here's why: the HStack had no height constraint of its own, so it accepted the proposed size from its parent (ScrollView), which is basically the full screen. Then .frame(maxHeight: .infinity) on each card happily stretched to fill that giant container.

So I had half the solution. The cards were matching each other's height. I just needed the HStack itself to stop being greedy and shrink down to "just tall enough to fit the tallest card."

The modifier I'd been avoiding

This is exactly what .fixedSize(horizontal: false, vertical: true) does. It tells a view: "ignore the proposed height from your parent—size yourself to your ideal content height instead."

ScrollView(.horizontal) {
    HStack(alignment: .top, spacing: 16) {
        ForEach(items) { item in
            CardView(item: item)
                .frame(maxHeight: .infinity, alignment: .topLeading)
        }
    }
    .fixedSize(horizontal: false, vertical: true) // šŸ‘ˆ the one line that fixes everything
}

And the result:

The problem

Exactly what the designer asked for. Every card matches the tallest one, and the row is no taller than it needs to be.

Why this actually works šŸ¤”?

It's worth pausing on the layout dance happening here, because it's a great example of how SwiftUI's parent-proposes-child-decides model plays out:

  1. ScrollView proposes its full height to the HStack.
  2. .fixedSize(vertical: true) intercepts that proposal and says: "thanks, but I'll size to my ideal height instead." The HStack's ideal height is the height of its tallest child.
  3. HStack then proposes that locked height down to each card.
  4. .frame(maxHeight: .infinity) on each card stretches every card to fill that height.

So the two modifiers aren't fighting each other—they're a tag team. .fixedSize decides what the height should be (just enough for the tallest card), and .frame(maxHeight: .infinity) decides how every other card relates to it (fill it).

Take either one away and the whole thing falls apart. Without .fixedSize, the stack goes full-screen. Without .frame(maxHeight: .infinity), the shorter cards stay short and you're back to a jagged row.

Mission accomplished āœ…

The real lesson

I'll be honest: this took me about an hour and a half to figure out. Not because the solution is hard—it's literally one line—but because I kept circling around .fixedSize instead of trying it. My brain had labeled it "dangerous" and refused to revisit that label.

The problem wasn't that the layout was tricky. It was that I'd shrunk my own toolbox.

The next time you catch yourself thinking "I never use that modifier"—stop and ask why. If the answer is "because the name sounds scary" or "I read a tweet once that said to avoid it," that's not a real reason. Every SwiftUI modifier exists because someone at Apple hit a layout problem it solves. Your job is to know which problem belongs to which tool.

Don't limit your solutions. The answer might be sitting right in front of you, wearing a name you've been ignoring šŸ˜…

subscribe for more like this

Subscribe for more