UIStackView properties according to size classes

689 views Asked by At

I try to test the UIStackView in a storyboard where I change the property "Axis" based on the orientation of an iPhone (wAny/hC, wC/hR).

Unfortunately, at running, the change is not applied. Specifically, the property "Axis" is modified (inspected in Xcode), but the layout will not change.

Thank you in advance for your help.

1

There are 1 answers

0
forgo On

This is definitely a bug. The behavior of the axis at runtime (simulator or device), after closer inspection, only changes for every other UIStackView in the view hierarchy.

I haven't tried every possible permutation, but this is what I am finding since the issue only cropped up once I wanted to introduce more than one stack view with this desired axis-changing behavior. There may be instances where a single stack view does not obey the size class rule, but I haven't reliably reproduced it.

Check out my iOS 9.1 project which clearly demonstrates this problem with 8 UIStackView elements in a vertical arrangement. They are all configured exactly the same other than their position constrained relative to each other and the top of the screen.

As you can see from the storyboard preview, things look as we want and expect in landscape:

enter image description here

It becomes a different story when running on the simulator or a device. After experimenting with many stack views, it becomes apparent that the bug affects every other stack view in the view hierarchy.

enter image description here

I have raised my own separate RDAR with this example project, despite hearing mention of another in the comments above. I could not find a public record of it.

Apparently there is a code-based workaround that is painful and totally against the purpose of autolayout, constraints, and size classes. This is a pretty major bug to me as it eliminates a pretty major use case for stack views and size classes.

The code workaround according to EinharchAltPlus's post in Apple Developer forums:

BTW: overriding viewWillTransitionToSize and manually setting different parameters solves the issue, but this is pretty tiresome as we need to check view in code as well, something that should be purely design related.

In case that's not entirely clear, you need to have a IBOutlet reference to the non-cooperative stack view and perhaps the view it fills in your view controller code. You can change the stack view's axis property and, ideally, the height of what it's contained in:

@IBOutlet weak var viewHackStack: UIView!
@IBOutlet weak var hackStackView: UIStackView!

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    if(self.traitCollection.verticalSizeClass == .Compact) {
        self.viewHackStack.frame.size.height = 25
        self.hackStackView.axis = .Horizontal
    }
    else if(self.traitCollection.verticalSizeClass == .Regular) {
        self.viewHackStack.frame.size.height = 50
        self.hackStackView.axis = .Vertical
    }
}

If you want to be more specific with the size class that determines the behavior change, just play around with the trait collection size class types until you've met your desired condition.

If I hear anything back on a bug fix timeline, I will post it here. Until then, I hope this helps as a workaround.