UIStackView and subview size classes

uistackview dynamic height
uistackview height based on content
swift stack view programmatically
uistackview distribution
uistackview intrinsic content size
uistackview alignment
uistackview not resizing height
uistackview height is ambiguous

I have a UIStackView where the middle button is only visible in a different size class storyboard view

After rotating the device the button will be visible because of its size class and is also in the right view hierarchy (image), but is has not enough constraints given by UIstackview to be positioned correctly, it is positioned on the upper left corner (label middle).

not enough constraints

The working buttons (not affected by size class changes) have much more constraints.

Is this a bug or am I missing something. Does anybody know a workaround?

It's definitely a bug in iOS. When the middle button is installed, it's being added as a subview of the stack view, but not as an arranged subview.

Here's a workaround. Set the custom class of the middle button to StackViewBugFixButton. Then connect the (new) priorView outlet of the middle button to the next button to the left. Here's the definition of StackViewBugFixButton:

StackViewBugFixButton.h
#import <UIKit/UIKit.h>

@interface StackViewBugFixButton : UIButton

@property (nonatomic, weak) IBOutlet UIView *priorSibling;

@end
StackViewBugFixButton.m
#import "StackViewBugFixButton.h"

@implementation StackViewBugFixButton

- (void)didMoveToSuperview {
    [super didMoveToSuperview];
    [self putIntoArrangedSubviewsIfNeeded];
}

- (void)putIntoArrangedSubviewsIfNeeded {
    if (![self.superview isKindOfClass:[UIStackView class]]) {
        return;
    }

    if (self.priorSibling == nil) {
        NSLog(@"%@ %s You forgot to hook up my priorSibling outlet.", self, __func__);
        return;
    }

    UIStackView *stackView = (UIStackView *)self.superview;
    if ([stackView.arrangedSubviews indexOfObject:self] != NSNotFound) {
        return;
    }

    NSUInteger priorSiblingIndex = [stackView.arrangedSubviews indexOfObject:self.priorSibling];
    if (priorSiblingIndex == NSNotFound) {
        NSLog(@"%@ %s My priorSibling isn't in my superview's arrangedSubviews.", self, __func__);
        return;
    }

    [stackView insertArrangedSubview:self atIndex:priorSiblingIndex + 1];
}

@end

You'll get one spurious warning of "You forgot to hook up my priorSibling outlet" because the view gets added as a subview of the stack view during loading, before its outlets have been connected.

UIStackView and subview size classes, It's definitely a bug in iOS. When the middle button is installed, it's being added as a subview of the stack view, but not as an arranged subview. I think you misunderstood, question says the same "UIStackView with 2 labels for iPhone and 3 labels for iPad using size classes". But thanks a lot, I got what I need with little amendments. – Bilal Nov 15 '17 at 12:34

Solutions offered by Rob and Uwe definitely work and address the issue of the conditionally-installed views not being added to arrangedSubviews on trait collection changes.

I preferred to address the issue without having to subclass every view that I wished to place inside a UIStackView though, so I subclassed UIStackView itself to override layoutSubviews() and add the orphaned subviews to arrangedSubviews:

class ManagedStackView: UIStackView {

    override func layoutSubviews() {
        super.layoutSubviews()
        let unarrangedSubviews = subviews.filter({ !arrangedSubviews.contains($0) })
        unarrangedSubviews.forEach({ insertArrangedSubview($0, atIndex: $0._arrangedSubviewIndex) })
    }

}


extension UIView {

    private var _arrangedSubviewIndex: Int {
        get {
            return tag
        }
        set {
            tag = newValue
        }
    }

}

UIStackView, class UIStackView : UIView The stack view uses Auto Layout to position and size its arranged views. The stack For all distributions except the UIStackView. The Stack View control (UIStackView) leverages the power of Auto Layout and Size Classes to manage a stack of subviews, either horizontally or vertically, which dynamically responds to the orientation and screen size of the iOS device.

Thanks to Rob I created the following solution with swift, you have to set controlIndex in user defined runtime values however to the right index (see image)

import UIKit

class UIButtonFixForStackView: UIButton {

    var controlIndex:Int = 0

    internal override func didMoveToSuperview() {


    if (superview?.isKindOfClass(UIStackView) == false)
    {
        return
    }

    if let stackView = self.superview as? UIStackView
    {
        if stackView.arrangedSubviews.indexOf(self) != nil
        {
            return
        }

        stackView.insertArrangedSubview(self, atIndex: controlIndex)
        }
    }
}

UIStackView and subview size classes, I have a UIStackView where the middle button is only visible in a different size class storyboard view After rotating the device the button will be visible because of  Instead, it just manages the position and size of its arranged views. As a result, some properties (like background Color) have no effect on the stack view. Similarly, you cannot override layer Class, draw(_:), or draw(_: in:). There are a number of properties that define how the stack view lays out its content.

While the accepted answers should work as a programmatic approach, it is worth noting that you can avoid this problem in Interface Builder by adding your size class variations for the Hidden property instead of the Installed property. iOS correctly handles this scenario because the arrangedSubviews never actually change, and UIStackView is built to adapt its layout when (arranged) subviews are hidden.

For example, if you want to hide a specific view for Compact widths, the subview's IB configuration would look like the following. Note that the view is Installed in all size classes:

UIStackView Tricks: Proportional Custom UIViews with 'Fill , How to use the Fill Proportionally option with any UIStackView custom view while class CustomView: UIView { var height = 1.0 override func  Finally, turn your attention to the one remaining task on your list. In landscape mode, vertical space is at a premium, so you want to bring the sections of the stack view closer together. To do this, you’ll use size classes to set the spacing of the top-level stack view to 10 instead of 20 when the vertical size class is compact.

UIStackView: Lessons learned - INLOOPX, supports size classes. Let's come to The Dark Side. Remove arranged subview. It is easy to insert an arranged  supports size classes; Prior to the view setup, you add any arranged subview to UIStackView and then you need to setup the constraints and pin the UIStackView to UIScrollView.

UIStackView Tutorial for iOS: Introducing Stack Views , Learn how to simplify your iOS layouts with UIStackView. Layout A stack view can distribute its subviews along its axis in various ways. of the top-level stack view to 10 instead of 20 when the vertical size class is compact. Animating UIStackView subview layout. Before we get onto the CloudKit part of this tutorial, we're going to add a bit more to our user interface. Specifically, we're going to add a "Tap to Play" button into the stack view, and have it animate so that it slides out when recording has finished.

Using Size Classes to Hide Stack View Contents, Using Size Classes to Hide Stack View Contents. Sep 27, 2015 · 2 minute read. Auto Layout · UIStackView · iOS 9. Last updated: Jun 12, 2020. I had an  Appetizer 🥗 UIStackView Essentials Introduced in iOS 9, UIStack View is the most recent addition to the UI control assortment in Cocoa Touch. On the surface, it looks similar to its older AppKit sibling, the NSStack View, but upon closer inspection, the differences between the two become clearer.

Comments
  • great, this worked perfectly, I changed it a little bit, because I don't like the outlet and drag drop but in essence its your solution, I will post it as answer to my own question