Deepak Sharma
Deepak Sharma

Reputation: 6477

UIStackView unsatisfiable autolayout constraints

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?

Upvotes: 1

Views: 1613

Answers (2)

DonMag
DonMag

Reputation: 77433

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),
            ])

    }   
}

Upvotes: 2

Josh Homann
Josh Homann

Reputation: 16327

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).

Upvotes: 0

Related Questions