How the relative dimension modifier interacts with stack views – Ole Begemann

How the relative dimension modifier interacts with stack views – Ole Begemann


I’ve yet one more factor to say on the relative sizing view modifier from my earlier publish, Working with percentages in SwiftUI format. I’m assuming you’ve learn that article. The next is sweet to know if you wish to use the modifier in your personal code, however I hope you’ll additionally study some common tidbits about SwiftUI’s format algorithm for HStacks and VStacks.

Utilizing relative sizing inside a stack view

Let’s apply the relativeProposed modifier to one of many subviews of an HStack:

HStack(spacing: 10) {
    Colour.blue
        .relativeProposed(width: 0.5)
    Colour.inexperienced
    Colour.yellow
}
.border(.main)
.body(top: 80)

What do you count on to occur right here? Will the blue view take up 50 % of the out there width? The reply isn’t any. In actual fact, the blue rectangle turns into narrower than the others:

It is because the HStack solely proposes a proportion of its out there width to every of its kids. Right here, the stack proposes one third of the out there house to its first youngster, the relative sizing modifier. The modifier then halves this worth, leading to one sixth of the entire width (minus spacing) for the blue shade. The opposite two rectangles then develop into wider than one third as a result of the primary youngster view didn’t deplete its full proposed width.

Replace Might 1, 2024: SwiftUI’s built-in containerRelativeFrame modifier (launched after I wrote my modifier) doesn’t exhibit this habits as a result of it makes use of the scale of the closest container view as its reference, and stack views don’t depend as containers on this context (which I discover considerably unintuitive, however that’s the way in which it’s).

Order issues

Now let’s transfer the modifier to the inexperienced shade within the center:

HStack(spacing: 10) {
    Colour.blue
    Colour.inexperienced
        .relativeProposed(width: 0.5)
    Colour.yellow
}

Naively, I’d count on an equal end result: the inexperienced rectangle ought to develop into 100 pt large, and blue and yellow ought to be 250 pt every. However that’s not what occurs — the yellow view finally ends up being wider than the blue one:

I discovered this unintuitive at first, nevertheless it is smart when you perceive that the HStack processes its kids in sequence:

  1. The HStack proposes one third of its out there house to the blue view: (620 – 20) / 3 = 200. The blue view accepts the proposal and turns into 200 pt large.

  2. Subsequent up is the relativeProposed modifier. The HStack divides the remaining house by the variety of remaining subviews and proposes that: 400 / 2 = 200. Our modifier halves this proposal and proposes 100 pt to the inexperienced view, which accepts it. The modifier in flip adopts the scale of its youngster and returns 100 pt to the HStack.

  3. For the reason that second subview used much less house than proposed, the HStack now has 300 pt left over to suggest to its remaining youngster, the yellow shade.

Necessary: the order wherein the stack lays out its subviews occurs to be from left to proper on this instance, however that’s not all the time the case. Generally, HStacks and VStacks first group their subviews by format precedence (extra on that beneath), after which order the views inside every group by flexibility such that the least versatile views are laid out first. For extra on this, see How an HStack Lays out Its Kids by Chris Eidhof. The views in our instance are all equally versatile (all of them can develop into any width between 0 and infinity), so the stack processes them of their “pure” order.

Leftover house isn’t redistributed

By now you could find a way guess how the format seems once we transfer our view modifier to the final youngster view:

HStack(spacing: 10) {
    Colour.blue
    Colour.inexperienced
    Colour.yellow
        .relativeProposed(width: 0.5)
}
  • Blue and inexperienced every obtain one third of the out there width and develop into 200 pt large. No surprises there.

  • When the HStack reaches the relativeProposed modifier, it has 200 pt left to distribute. Once more, the modifier and the yellow rectangle solely use half of this quantity.

The tip result’s that the HStack finally ends up with 100 pt left over. The method stops right here — the HStack does not begin over in an try to discover a “higher” answer. The stack makes itself simply sufficiently big to comprise its subviews (= 520 pt incl. spacing) and stories that dimension to its dad or mum.

Structure precedence

We are able to use the layoutPriority view modifier to affect how stacks and different containers lay out their kids. Let’s give the subview with the relative sizing modifier a better format precedence (the default precedence is 0):

HStack(spacing: 10) {
    Colour.blue
    Colour.inexperienced
    Colour.yellow
        .relativeProposed(width: 0.5)
        .layoutPriority(1)
}

This ends in a format the place the yellow rectangle truly takes up 50 % of the out there house:

Clarification:

  1. The HStack teams its kids by format precedence after which processes every group in sequence, from highest to lowest precedence. Every group is proposed the whole remaining house.

  2. The primary format group solely incorporates a single view, our relative sizing modifier with the yellow shade. The HStack proposes your complete out there house (minus spacing) = 600 pt. Our modifier halves the proposal, leading to 300 pt for the yellow view.

  3. There are 300 pt left over for the second format group. These are distributed equally among the many two kids as a result of every subview accepts the proposed dimension.

Conclusion

The code I used to generate the pictures on this article is accessible on GitHub. I solely checked out HStacks right here, however VStacks work in precisely the identical approach for the vertical dimension.

SwiftUI’s format algorithm all the time follows this primary sample of proposed sizes and responses. Every of the built-in “primitive” views (e.g. mounted and versatile frames, stacks, Textual content, Picture, Spacer, shapes, padding, background, overlay) has a well-defined (if not all the time well-documented) format habits that may be expressed as a perform (ProposedViewSize) -> CGSize. You’ll have to study the habits for view to work successfully with SwiftUI.

A concrete lesson I’m taking away from this evaluation: HStack and VStack don’t deal with format as an optimization drawback that tries to search out the optimum answer for a set of constraints (autolayout model). Fairly, they kind their kids in a selected approach after which do a single proposal-and-response move over them. If there’s house leftover on the finish, or if the out there house isn’t sufficient, then so be it.

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.