1

I have a UIStackView defined in storyboard with the first button's height set to 70 and other one set to 45. I get this autolayout error:

 [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
    (1) look at each constraint and try to figure out which you don't expect; 
    (2) find the code that added the unwanted constraint or constraints and fix it. 
 (
"<NSLayoutConstraint:0x280f614f0 UIButton:0x10641a120.height == 45   (active)>",
"<NSLayoutConstraint:0x280f60e60 UIButton:0x106418f80.height == 70   (active)>",
"<NSLayoutConstraint:0x280f604b0 'UISV-alignment' UIButton:0x10641a120.bottom == UIButton:0x106418f80.bottom   (active)>",
"<NSLayoutConstraint:0x280f63cf0 'UISV-alignment' UIButton:0x10641a120.top == UIButton:0x106418f80.top   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x280f60e60 UIButton:0x106418f80.height == 70   (active)>

I understand the UIStackView is unable to accept different heights of UIButtons, is that correct and what is the way to have UIStackView accept different heights or widths of it's elements?

Deepak Sharma
  • 5,577
  • 7
  • 55
  • 131

2 Answers2

2

Something in your Stack View constraints is causing the problem.

Here is a valid layout:

enter image description here

With the Stack View properties:

enter image description here

The result before adding a third button via code:

enter image description here

And the result after adding a third button (height constraint of 60) via code:

enter image description here

No auto-layout warnings or errors.

The code (connected to Button 1), adds / removes Button 3 as an arranged subview of the stack view:

class TestViewController: UIViewController {

    @IBOutlet var theStackView: UIStackView!

    var thirdButton: UIButton = {
        let b = UIButton()
        b.translatesAutoresizingMaskIntoConstraints = false
        b.setTitle("Button 3", for: .normal)
        b.backgroundColor = .red
        return b
    }()

    @IBAction func doAddThird(_ sender: Any) {

        if theStackView.arrangedSubviews.count == 2 {

            theStackView.addArrangedSubview(thirdButton)

        } else {

            if let v = theStackView.arrangedSubviews.last {
                v.removeFromSuperview()
            }

        }

    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // finish initializing the third button
        if let v = theStackView.arrangedSubviews.first as? UIButton {
            thirdButton.titleLabel?.font = v.titleLabel?.font
        }

        NSLayoutConstraint.activate([
            thirdButton.heightAnchor.constraint(equalToConstant: 60),
            ])

    }   
}
DonMag
  • 69,424
  • 5
  • 50
  • 86
0

A Stackview will take on the dimensions of its components, if you don't give it height and width constraints. It looks like you are telling your stackView to be a particular height (either because you have a height constraint or two Y position constraints which imply a height). You cannot both tell the stackView to have a height and tell all of its arrangedSubviews to have a height unless those two values are exactly the same. So for instance if you tell the stack to be 150 high, and your buttons are 45 and 70 high then the one with the lowest content hugging priority loses and gets expanded to take up the extra 35 points of space that the stack view needs to be 150 points high.

Quick solutions:

  1. Remove the hieght constraint on the stack view; it will now be as high as the sum of the high of its elements.
  2. Add a blank UIView to the stack and give it content hugging of 1 and it will take up any extra space (this only works if your stack is bigger than its elements; if its too small you need to reduce the size of the arrangedSubviews instead).
Josh Homann
  • 15,933
  • 3
  • 30
  • 33
  • The stackview axis is horizontal, so the height of individual element need not be specified in constraint? – Deepak Sharma Dec 05 '18 at 17:06
  • Then you probably have alignment set to something like .fill and need .top or .center instead. https://developer.apple.com/documentation/uikit/uistackview/alignment – Josh Homann Dec 05 '18 at 17:09
  • It never works if I specify height and width of the UIButtons, no matter what. Autolayout errors keep popping up. – Deepak Sharma Dec 05 '18 at 19:57
  • You need to post the constraints and properties for your stack view or the code that creates them. – Josh Homann Dec 05 '18 at 19:59
  • I do not set any constraints on UIStackView. All I do is programmatically insert a third UIButton in the UIStackView that has two UIButtons of height 60. The inserted button has width and height constraints set to 45. – Deepak Sharma Dec 05 '18 at 20:06
  • Deepak, try setting your constraints to priority 999, or at least for those constraints that it's complaining about. All the constraints created by the UIStackView will be priority 1000. This way they won't collide. (I got this trick from an NSHipster blog post. Hope this helps someone!) – Smartcat Jan 26 '21 at 01:12