1

I've a fairly difficult layout design that might be easier using nested stack views in iOS. BUT, I'm having problems controlling the size or distribution of stacks nested inside other stacks. One part of the layout, for example, looks OK-ish if I set Distribution to Fit Equally: enter image description here

BUT, what I really want is the photo and container to be about 1/3 the width of the text field stack. If I set Distribution to Fit Proportionally, the stack with the image (which doesn't change size) and container spreadout and squash the text against the side of the display. Everything I read suggests to reduce the Content Compression Resistance Priority. I've tried this on the image, the container and on the stack view itself, but it doesn't do much.

Could someone please point me in the right direction to control the relative widths of stacks nested inside other stacks?

DrWhat
  • 2,360
  • 5
  • 19
  • 33

3 Answers3

2

To answer your question: Could someone please point me in the right direction to control the relative widths of stacks nested inside other stacks? ...

The problem is that your top-level UIStackView is asking for the intrinsicContentSize of all its subviews when determining how to divy up extra space/squash things together, based on each of the subviews' returned intrinsicContentSize and their contentHuggingPriority/contentCompressionResistance. Unfortunately, UIStackView itself - ie your nested UIStackView - doesn't return anything useful for its own intrinsicContentSize (despite it having its own subviews, which do). So the top-level UIStackView just ploughs ahead and lays things out as though the nested one doesn't care, which is why your nested UIStackView is ending up wider than you'd like.

I've had good luck by using a simple UIStackView subclass for the nested one which returns a useful intrinsicContentSize based on its own contents' widths (for a vertical axis) or heights (for a horizonal axis), as follows:

@implementation NestedStackView
- (CGSize)intrinsicContentSize
{
    CGSize size = [super intrinsicContentSize]; // returns {UIViewNoIntrinsicMetric,UIViewNoIntrinsicMetric}

    for (UIView *view in self.arrangedSubviews)
        if (!view.hidden) { // UIStackView ignores hidden subviews when doing its layout; so should we...
            if (self.axis == UILayoutConstraintAxisVertical) {
                size.width = MAX(view.intrinsicContentSize.width, size.width);
            } else {
                size.height = MAX(view.intrinsicContentSize.height, size.height);
            }
        }
    return size;
}
@end

By doing so, the top-level UIStackView now takes the nested UIStackView's desired content width into account when allocating its space. [aside: I first attempted adding an explicit NSLayoutConstraint on the width of the nested UIStackView, but it was just ignored. Whereas returning an instrinsicContentSize.width worked]

tiritea
  • 1,229
  • 13
  • 18
1

I'd hope there might be a better answer, but the only effective method that I've found is to put the image into a view and pin it to the view's edges. The size of the view can then be controlled with constraints.

If anyone has a better approach, they can still answer as part of this more detailed question: https://stackoverflow.com/questions/33875801/how-to-position-and-size-images-and-their-frames-in-nested-stack-views-in-ib-w

Community
  • 1
  • 1
DrWhat
  • 2,360
  • 5
  • 19
  • 33
  • I'm struggling with this as well. My HTML/CSSd intuitions are a hindrance. But one tip I can offer is I think you can use a UILayoutGuide as a lighter weight alternative to UIView if your only concern is layout. – aehlke Dec 09 '15 at 01:41
0

One easy way to do this is to use AutoLayout to set relative width constraints on your nested stack views. For example, if you want to have your left UIStackView to fill 2/3rds of the screen and your right UIStackViewto fill 1/3rd, you can use the code below.

let leftStackView = UIStackView(arrangedSubviews: [...])
leftStackView.axis = .vertical

let rightStackView = UIStackView(arrangedSubviews: [...])
rightStackView.axis = .vertical

let containerStackView = UIStackView(arrangedSubviews:
                                     [leftStackView, rightStackView])

containerStackView.axis = .horizontal
containerStackView.distribution = .fillProportionally
containerStackView.translatesAutoresizingMaskIntoConstraints = false

addSubview(containerStackView)

//leftStackView will be twice as wide as the rightStackView, so 
//the distribution is 2/3rds to 1/3rd
leftStackView.widthAnchor.constraint(equalTo:
  rightStackView.widthAnchor, multiplier: 2.0).isActive = true
gregkerzhner
  • 736
  • 7
  • 18