How to set weight in UIStackView in IOS
Asked Answered
K

3

15

UIStackView is similar to Android LinearLayout but I could not figure out how to set weight for the subviews.

Suppose I have a vertical UIStackView and 3 UIImageViews in it. I want to set weights 3, 6, 1 consecutively for the UIImageViews. How do I do that?

layout diagram

Kazimir answered 8/12, 2015 at 16:15 Comment(1)
This is completely trivial. You just use height == height of the stack view and set the fraction.Intelligentsia
C
11

UIStackView doesn't have the same concept of weights. It can use a subview's intrinsicContentSize as a weight, but setting a specific intrinsicContentSize typically requires making a subclass and it's used in other situations too (unlike the android:layout_weight attribute you're familiar with, which is only used by LinearLayout).

But since UIStackView works by applying constraints to its arranged subviews, you can get the effect of weights by setting additional constraints between the heights of the subviews. (UIStackView is designed to let you add your own constraints to tweak the layout this way.)

In your case, you want to constrain the height of the top view to be 3 times the height of the bottom view, and you want to constrain the height of the middle view to be 6 times the height of the bottom view.

You can set up a proportional-height constraint in a storyboard by creating an equal-height constraint, then editing the constraint's multiplier.

In code, you could do it like this (on iOS 9.0 or later):

NSLayoutConstraint.activateConstraints([
    top.heightAnchor.constraintEqualToAnchor(bottom.heightAnchor, multiplier: 3),
    middle.heightAnchor.constraintEqualToAnchor(bottom.heightAnchor, multiplier: 6),
])
Classroom answered 8/12, 2015 at 16:33 Comment(8)
"UIStackView doesn't have a concept of weights" Actually, yes it does. But the weight is an inherent quality of the arranged view.Noman
can we not set the distribution property of stackview to fillProportionallyAdjure
If you use fillProportionally, you have to set each view's intrinsicContentSize to the weight you want. Since intrinsicContentSize is a readonly property, that means creating UIView subclasses to override intrinsicContentSize. If you use constraints, you don't have to create subclasses.Classroom
Stack view does not, inherently, have a weight concept built-in. Because, you just use height == height of the stack view and set the fraction. This is completely commonplace and used everywhere. (In the example you'd set the three fractions to ".3, .6, .1", obviously.) If stack view had a weighting array built-in, we'd all be saying "that's totally bizarre because obviously you just set the height of each element as a fraction anyway".Intelligentsia
When the stack view's distribution is set to .fillProportionally, it treats each arranged subview's intrinsicContentSize as a weight. “Views are resized proportionally based on their intrinsic content size along the stack view’s axis.” If you don't think that is a “weight concept” or “built-in”, then I guess we have different definitions of those words. 🤷‍♂️Classroom
Also, constraining each arranged subview's height to a fraction of the stack view's height is not necessarily a good solution if the stack view's spacing is not zero. For example, if you constrain the subviews to 0.3, 0.6, and 0.1 times the stack view's height (as in the original question), and set the spacing to 10, you'll get unsatisfiable constraint errors. That's why my answer constrains the subviews' heights to each other, not to the stack view's height.Classroom
In answer, you have mentioned equal width instead of equal height. and you can do it directly in the storyboard with a stack view. Just add vertical stack view then add 3 views and set distribution to fill and drag equal height from bottom to middle. set multiplier 1/6 then drag equal height from bottom to top and set multiplier 1/3Crescendo
Thanks for pointing out that I said “equal-width” instead of “equal-height”. I have corrected the error.Classroom
I
5

Intrinsic content size, etc, is totally uninvolved.

To set fractional heights, just set fractional heights:

  1. Fix the height of the stack view (say, the whole screen)

  2. Put in the three views A, B, C

  3. For A, make an height constraint 0.3 of the height of the stack view

  4. For B, make an height constraint 0.6 of the height to the stack view

Set the stack view to distribution:Fill.

If you run this on a powerful iPhone, it will figure out that C is "0.1".

You're done.

Intelligentsia answered 26/7, 2019 at 18:37 Comment(0)
N
0

First off, do you really need a stack view for this? It would be much easier to arrange this simply using proportional height constraints directly.

However, you can do this with a stack view if you really want to use a stack view. The secret is that the "weight" in question is simply the arranged view's intrinsicContentSize().height. Knowing this, I was easily able to set up a stack view consisting of three image views in the proportions you request:

enter image description here

Those, for purposes of the demonstration, are the same image repeated three times: one at 3x height, one at 6x height, and one at 1x height.

How did I do it? I gave the three image views tag values of 300, 600, and 100 respectively in the storyboard. (Of course I could have used an IBInspectable custom property for this, and in real life, I would do so.) Then I made them all instances of my UIImageView subclass, MyImageView, whose code looks like this:

class MyImageView: UIImageView {
    override func intrinsicContentSize() -> CGSize {
        print(self.tag)
        return CGSizeMake(CGFloat(self.tag), CGFloat(self.tag))
    }
}

The stack view's Distribution is configured as Fill Proportionally. The image views are configured with their Content Mode as Scale To Fill. Result: The stack view, in laying out its arranged views, consults the intrinsicContentSize method, and thus does the right thing.

Noman answered 8/12, 2015 at 17:8 Comment(2)
I wasn't suggesting using only constraints. I was suggesting adding constraints while also using a stack view. I think that's simpler than subclassing to override intrinsicContentSize. Stack views on both iOS and Mac OS X are explicitly designed to let you use constraints to tweak the layout.Classroom
@robmayoff I agree with you about what he should do. I'm just showing that the thing he specified can in fact be done.Noman

© 2022 - 2024 — McMap. All rights reserved.