0

I am using the PureLayout framework and writing my UIView in Swift.

I have a view, which contains four buttons that are not all the same height.

I have added constraints to equally space these buttons, and center them vertically within the view, and added the padding on either side of the buttons on the end. However, I'm not sure how to set the height / top constraints of the view so that it has an 8.0 point gap between it and the top of the tallest button. I can add the constraints to the top and bottom of each button so that they are all >= 8.0 pixels from the edge of the view, but that would mean that the view could stretch vertically while still maintaining those constraints. I won't know which of these buttons is the tallest until runtime.

The only option I can think of is iterating through my buttons before hand and finding the tallest to store in a variable, and using that while making my constraints, but I was wondering if there's a way to do this with PureLayout a bit more simply, without having to run through all of those objects. Is there a method I'm missing that says something like "Make this value as small as possible while satisfying the rest of the constraints" that I could apply to the height of the view, instead?

let topOffset = CGFloat(8.0)
let bottomOffset = CGFloat(8.0)
button1.autoConstrainAttribute(.top, to: .top, of: self, withOffset: topOffset, relation: NSLayoutRelation.self.autoConstrainAttribute(.bottom, to: .bottom, of: button1, withOffset: topOffset, relation: NSLayoutRelation.greaterThanOrEqual)
button2.autoConstrainAttribute(.top, to: .top, of: self, withOffset: topOffset, relation: NSLayoutRelation.self.autoConstrainAttribute(.bottom, to: .bottom, of: button2, withOffset: topOffset, relation: NSLayoutRelation.greaterThanOrEqual)
button3.autoConstrainAttribute(.top, to: .top, of: self, withOffset: topOffset, relation: NSLayoutRelation.self.autoConstrainAttribute(.bottom, to: .bottom, of: button3, withOffset: topOffset, relation: NSLayoutRelation.greaterThanOrEqual)
button4.autoConstrainAttribute(.top, to: .top, of: self, withOffset: topOffset, relation: NSLayoutRelation.self.autoConstrainAttribute(.bottom, to: .bottom, of: button4, withOffset: topOffset, relation: NSLayoutRelation.greaterThanOrEqual)
//MISSING: Somehow make self's height as small as possible

I apologize for the sloppiness, but here is in image of what I'm aiming for sort of. Ignore the horizontal spacing. The rectangle is the view, and the circles are the buttons, with varying sizes. The view needs to have 8.0 points of padding above/below the tallest of these buttons, i.e. the blue one in this case. They are all centered vertically within the view, and I don't know their sizes until I dynamically set the images while instantiating the view.

Rectangle

Jake T.
  • 4,308
  • 2
  • 20
  • 48

2 Answers2

1

For what I can understand, this is what you are trying to achieve, right?

To achieve this, you could use an UIStackView with equal spacing as a container, and add all your buttons as children to it. The stack view will automatically assume the height of the tallest button, and that you can space out 8px from the top.

Image

  • Ahh, I have never used a StackView before, and that sounds like a perfect piece of this puzzle. Wasn't familiar with the concept, to be honest, have always used spacer views until I discovered PureLayout with it's convenient multiplier based constraints. I'll try reworking these into a stackview and see how that goes. The only difference between what I'm aiming for and what you've put here is that the buttons are all vertically aligned, and the space above/below the tallest, which you said can easily be added around the stackview. – Jake T. Aug 16 '17 at 21:54
1

You could use a mixture of a container view and a stackview like this:

override func viewDidLoad() {
    super.viewDidLoad()

    let container = UIView()
    container.translatesAutoresizingMaskIntoConstraints = false

    container.backgroundColor = .lightGray

    view.addSubview(container)
    container.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    container.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

    let stackView = UIStackView()
    stackView.translatesAutoresizingMaskIntoConstraints = false

    stackView.axis = .horizontal
    stackView.alignment = .center
    stackView.distribution = .fillProportionally
    stackView.spacing = 8

    container.addSubview(stackView)
    stackView.topAnchor.constraint(equalTo: container.topAnchor, constant: 8).isActive = true
    stackView.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 8).isActive = true
    container.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 8).isActive = true
    container.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 8).isActive = true

    let buttonHeights: [CGFloat] = [30, 50, 40, 60]

    for (i, buttonHeight) in buttonHeights.enumerated() {
        let button = RoundButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false

        button.backgroundColor = .darkGray
        button.setTitle("\(i)", for: .normal)
        button.setTitleColor(.white, for: .normal)

        stackView.addArrangedSubview(button)
        button.heightAnchor.constraint(equalToConstant: buttonHeight).isActive = true
        button.widthAnchor.constraint(equalTo: button.heightAnchor).isActive = true
    }
}

Note: My custom RoundButton class simply sets the corner radius to make it a round button.

Result: result

André Slotta
  • 13,774
  • 2
  • 22
  • 34