Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIStackView's constraints break when spacing is set

XCode is throwing up the following constraint error when I attempt to use a UIStackView:

(
    "<NSAutoresizingMaskLayoutConstraint:0x7f87a1dfa360 h=--& v=--& V:[UIStackView:0x7f87a6403a00(0)]>",
    "<NSLayoutConstraint:0x7f87a6410590 'UISV-canvas-connection' UIStackView:0x7f87a6403a00.top == UIView:0x7f87a6409630.top>",
    "<NSLayoutConstraint:0x7f87a6444170 'UISV-canvas-connection' V:[UIView:0x7f87a644d790]-(0)-|   (Names: '|':UIStackView:0x7f87a6403a00 )>",
    "<NSLayoutConstraint:0x7f87a645bec0 'UISV-fill-equally' UIView:0x7f87a6407a10.height == UIView:0x7f87a6409630.height>",
    "<NSLayoutConstraint:0x7f87a6458f40 'UISV-fill-equally' UIView:0x7f87a644d790.height == UIView:0x7f87a6409630.height>",
    "<NSLayoutConstraint:0x7f87a64306d0 'UISV-spacing' V:[UIView:0x7f87a6409630]-(5)-[UIView:0x7f87a6407a10]>",
    "<NSLayoutConstraint:0x7f87a643bea0 'UISV-spacing' V:[UIView:0x7f87a6407a10]-(5)-[UIView:0x7f87a644d790]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f87a643bea0 'UISV-spacing' V:[UIView:0x7f87a6407a10]-(5)-[UIView:0x7f87a644d790]>

My view controller is as follows:

public class ExampleController: UIViewController {

    let v1 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 100))

    let v2 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 300))

    let v3 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 200))

    let parent1 = UIStackView()


    public override func viewDidLoad() {
        super.viewDidLoad()

        v1.backgroundColor = .red
        v2.backgroundColor = .green
        v3.backgroundColor = .blue

        parent1.axis = .vertical
        parent1.distribution = .fillEqually
        parent1.spacing = 5 // TODO: This causes the error!

        parent1.addArrangedSubview(v1)
        parent1.addArrangedSubview(v2)
        parent1.addArrangedSubview(v3)

        view.addSubview(parent1)
    }

    // MARK: Constraints

    private var didUpdateConstraints = false

    override public func updateViewConstraints() {
        if !didUpdateConstraints {

            parent1.snp.makeConstraints { (make) -> Void in
                make.edges.equalToSuperview()
            }

            didUpdateConstraints = true
        }
        super.updateViewConstraints()
    }
}

The distribution of the stack view doesn't seem to make a difference. Whenever spacing is set, I receive the error.


  1. What is the conflict with my constraints?

  2. What is UISV-canvas-connection?

like image 469
sdasdadas Avatar asked Feb 03 '26 00:02

sdasdadas


1 Answers

It's actually not breaking because of the spacing. It's breaking because of the way you are creating the views :D

When you create a view like...

let v1 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 100))

When the view gets laid out it gets a default set of constraints added to it. The same is true for the UIStackView.

When you create it like...

let parent1 = UIStackView()

It gets a frame of (0, 0, 0, 0) and then constraints added to it to keep that frame position and height.

The error you are getting is because of the height and width of the stack view.

<NSAutoresizingMaskLayoutConstraint:0x7f87a1dfa360 h=--& v=--& V:[UIStackView:0x7f87a6403a00(0)]>

This line is referring to those "automatic" constraints.

Your best option is to recreate the views widths and height using constraints.

Like this...

// using this format means you can create the view and add all the
// other config to it at the same time
let v1: UIView = {
    let u = UIView()

    // this line stops the "automatic" constraints being added
    u.translatesAutoresizingMaskIntoConstraints = false

    u.backgroundColor = .red

    // now you have to add your own constraints though
    u.heightAnchor.constraint(equalToConstant: 100).isActive = true
    u.widthAnchor.constraint(equalToConstant: 250).isActive = true
    return u
}()

let v2: UIView = {
    let u = UIView()
    u.translatesAutoresizingMaskIntoConstraints = false
    u.backgroundColor = .green
    u.heightAnchor.constraint(equalToConstant: 100).isActive = true
    u.widthAnchor.constraint(equalToConstant: 250).isActive = true
    return u
}()

let v3: UIView = {
    let u = UIView()
    u.translatesAutoresizingMaskIntoConstraints = false
    u.backgroundColor = .blue
    u.heightAnchor.constraint(equalToConstant: 100).isActive = true
    u.widthAnchor.constraint(equalToConstant: 250).isActive = true
    return u
}()

let parent1: UIStackView = {
    let s = UIStackView()
    s.translatesAutoresizingMaskIntoConstraints = false
    s.spacing = 5
    s.axis = .vertical
    s.distirbution = .fillEqually
    return s
}()

public override func viewDidLoad() {
    super.viewDidLoad()

    // I moved all the config stuff into the creation of each view...
    // No need to have them here.

    parent1.addArrangedSubview(v1)
    parent1.addArrangedSubview(v2)
    parent1.addArrangedSubview(v3)

    view.addSubview(parent1)
}

A word of warning here though.

You are using .fillEqually in the stack view. This will give each view heights equal to each other. This is going to conflict with the height constraint you added to them. Maybe you should remove the constraints from each view entirely and let the stack view do the layout.

Like this...

let v2: UIView = {
    let u = UIView()
    u.translatesAutoresizingMaskIntoConstraints = false
    u.backgroundColor = .green
    return u
}()
like image 194
Fogmeister Avatar answered Feb 05 '26 12:02

Fogmeister



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!