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?

Richard Topchii
  • 7,075
  • 8
  • 48
  • 115

2 Answers2

5

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()
}
Richard Topchii
  • 7,075
  • 8
  • 48
  • 115
  • 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) – Alladinian Jan 09 '18 at 14:21
  • No, it worked fine: https://user-images.githubusercontent.com/8013017/34727078-824d70be-f55e-11e7-8bbc-f5934164e848.png – Richard Topchii Jan 09 '18 at 15:00
  • 1
    I use `equalCentering` distribution – Richard Topchii Jan 09 '18 at 15:00
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.

Alladinian
  • 34,483
  • 6
  • 89
  • 91