[SC]()

iOS. Apple. Indies. Plus Things.

Modifier Monday: .fixedSize()

// Written by Jordan Morgan // Feb 7th, 2022 // Read it in about 2 minutes // RE: SwiftUI

This post is brought to you by Emerge Tools, the best way to build on mobile.

Welcome to Modifier Monday! Today’s modifier is .fixedSize(), and its docs are here:

/// Fixes this view at its ideal size in the specified dimensions.
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)

@inlinable public func fixedSize() -> some View
@inlinable public func fixedSize(horizontal: Bool, vertical: Bool) -> some View

Let’s see it in action.

Examples

Let’s start with Text using no extra modifiers, but with a string that has a line break:

var body: some View {
    Text("Welcome to Swiftjective-C!\nThe Shortest and least complicated domain name ever.")
}

Text control in SwiftUI

Padded and pretty by default. Let’s tack on .fixedSize() and see what happens:

var body: some View {
    Text("Welcome to Swiftjective-C!\nThe Shortest and least complicated domain name ever.")
        .fixedSize()
}

Resulting in:

Text control in SwiftUI using the fixed size modifier.

Hmmm, it’s now extended beyond the bounds of the view, interesting!

Let’s try a Circle() shape. Here’s a plain, blue circle and its default layout:

var body: some View {
    Circle()
        .fill(Color.blue)
}

A blue Circle view in SwiftUI.

What do you think happens if we tack .fixedSize() on it?

var body: some View {
    Circle()
        .fill(Color.blue)
        .fixedSize()
}

We get:

A blue Circle view in SwiftUI using fixed size.

Oh, wow - #TinyCircle.

Let’s go try one more control, a DatePicker, before we uncover why this is happening:

var body: some View {
    DatePicker("Pick a Date",
               selection: .constant(.now))
}

For that, SwiftUI lays things out like so:

A DatePicker view in SwiftUI.

And with .fixedSize():

A DatePicker view in SwiftUI using fixed size.

Three views, and three different results.

WTFixedSize Is Happening?

To comprehend how .fixedSize() works, you have to remember the dance SwiftUI does when it lays out its hierarchy. Parents propose a size to children, and they can either take it or leave it. That, and the fact that views can size themselves a few different ways:

  1. They could expand.
  2. They could have an ideal size (i.e. intrinsic content size, like in UIKit)
  3. They could already have a set size.

So, if you use .fixedSize() on a view already showing at its “ideal size”, then the modifier virtually has no effect. But, if it’s not, then it kicks in and sizes the control exactly to its specifications.

For Text, that’s the length of the string. That’s why we saw the .fixedSize() version extending beyond the screen - because its ideal size is the text that it contains.

For a Circle, well - whose to say how big or small a “default ciricle” is? So we get that tiny one, instead of it filling to expand its proposed available space.

For the DatePicker, it nukes that padding between the prompt and the trailing buttons. Because a DatePicker just has those three controls, and that’s all it needs space for.

So it is, .fixedSize() kicks in a control’s ideal size, content size, true size – call it whatever helps you understand it best.

Or, put another way: .fixedSize() does a two step layout calculation:

  1. It takes the parent’s proposal and throws it out the proverbial window.
  2. Then, it then sizes itself exactly the way that’s best for itself - but only just what it needs and no more.

Thinking on it now, this modifier would’ve made more sense to me if it was called .idealSize() — because that’s essentially what it’s doing under the hood. Or, further, how about .justTakeTheSpaceYouNeedAndNoMore(), but I guess that probably doesn’t roll of the tongue as well.

The canonical example Apple seems to use to demonstrate this1 is a Text view with a fixed size and a frame. Visually, you can see the Text ignore that frame’s proposed size, and instead just size itself to what its size really is without the parent’s input (again, the length of the string the Text contains):

var body: some View {
    Text("I'll size myself how I want, thanks.")
        .fixedSize()
        .frame(width: 100, height: 100)
        .border(Color.red)
}

A Text view extending beyond its bounds due to fixed size.

And that’s really it. No need to sit and stare at the screen with a ruminative face when figuring out .fixedSize() - it’s just giving you the view’s true and perfect size. Just the space that it needs, and nothing else.

Until next time ✌️

  1. It’s found in their docs for this API. 

···

Spot an issue, anything to add?

Reach Out.