UIStackView: push subviews to the edges ("Unlimited" spacing)
Asked Answered
B

2

8

I am using UIStackView to define a list's layout.

I am trying to achieve the effect similar to this:

||Item1---spacing---Item2||

So, the two items are being pushed to the sides of the UIStackView.

However, the items are grouped together in every possible combinations of distribution and alignment:

||Item1-Item2-----spacing-----||

I tried setting a large spacing value, i.e. 500. In that case, Item2 just goes off-screen.

Is there any way to "tie" Item1 and Item2 to the left and right sides of the UIStackView, while allowing spacing to change depending on the screen width?

Branscum answered 9/1, 2018 at 13:54 Comment(0)
B
6

The UIStackView collapsed and set it's intrinsicContentSize to be the sum of it's subviews and spacings.

By adding a set of constraints to "stretch" the UIStackView, I managed to get the effect I wanted:

hStack.snp.makeConstraints { (make) in
  make.leading.trailing.equalToSuperview()
}
Branscum answered 9/1, 2018 at 14:17 Comment(3)
Cool... but the thing is that (at least in my tests) your left view will be stretched as well to the point that it reaches your right view (you can fire up the view debugger or set background colors to visuallize it)Ashmore
No, it worked fine: user-images.githubusercontent.com/8013017/…Branscum
I use equalCentering distributionBranscum
A
3

One possible way to achieve that would be adding a 'spacing' view between your two views, and make sure that hugging priority of your views would be greater than the spacing one (and of course that they would have a valid intrinsic size or constraints that define their width). Here is a quick playground I've put together to test this:

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let label1 = UILabel()
        label1.translatesAutoresizingMaskIntoConstraints = false
        label1.text = "Left"
        label1.backgroundColor = .red
        label1.setContentHuggingPriority(.required, for: .horizontal)

        let label2 = UILabel()
        label2.translatesAutoresizingMaskIntoConstraints = false
        label2.text = "Right"
        label2.backgroundColor = .green
        label2.setContentHuggingPriority(.required, for: .horizontal)

        let stack = UIStackView(arrangedSubviews: [label1, UIView(), label2])
        stack.translatesAutoresizingMaskIntoConstraints = false
        stack.distribution = .fill
        stack.axis = .horizontal

        view.addSubview(stack)

        stack.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        stack.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        stack.topAnchor.constraint(equalTo: view.topAnchor).isActive = true


        self.view = view
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

Which produces this:

enter image description here


Note: It's interesting to note that the result in my example stays the same even if hugging priority of the labels is not set at all (I would love to know exactly why this is happening btw. My guess is because they have a non zero intrinsic content size(?)), but decided to leave the code in order to demonstrate the principle behind it.

Ashmore answered 9/1, 2018 at 14:6 Comment(1)
That might work, however, I've achieved the effect I aimed for by adding a separate set of constraints for the StackViewBranscum

© 2022 - 2024 — McMap. All rights reserved.