Search code examples
iosswiftnslayoutconstraintios-autolayout

Programatically setting auto layout constraints for a UIView's subviews


Below I have included some code for you to checkout. I am trying to take a custom UIView and add another custom subview to it. This subview should be constrained to the parent view in such a way it essentially just lays on top with the same dimensions and the parent just acts as a wrapper.

I have tried using the NSLayoutConstraint and failed miserable so far. The view never actually shows up. I have a left, right, bottom, and top constraint which should line up with the parent view.

The first ask I have is that someone please explain and or correct my logic when using the following method. The item param I figured is the actual view you want to set a constraint for (customViewChild). The attribute is to say that I want the left edge of my customViewChild to be used for this constraint. The relatedBy seems pretty straight forward although I could be wrong, and then finally the toItem points to self which is my CustomViewParent which also has a .left attribute to say that I want the left edge of my child and parent to line up. Is this logic flawed or am I doing something else wrong?

NSLayoutConstraint(item: customViewChild!, 
            attribute: .left, 
            relatedBy: .equal,
            toItem: self,
            attribute: .left, 
            multiplier: 1.0, 
            constant: 0.0)

I know the following example could very easily be done with IB, but I am trying to understand NSLayoutConstraint, so please provide answers regarding that. And lastly, if anyone could actually correct this code so I have a working example, that would be awesome.

class CustomViewParent: UIView {

    var customViewChild: UIView?

    override init(frame: CGRect) {
        super.init(frame: frame)

        setConstraints()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        setConstraints()
    }

    func setConstraints() {
        customViewChild = UIView()

        addSubview(customViewChild!)
        customViewChild?.translatesAutoresizingMaskIntoConstraints = false

        let leftConstraint = NSLayoutConstraint(item: customViewChild!, 
            attribute: .left, 
            relatedBy: .equal,
            toItem: self,
            attribute: .left, 
            multiplier: 1.0, 
            constant: 0.0).isActive = true

        let rightConstraint = NSLayoutConstraint(item: customViewChild!, 
            attribute: .right, 
            relatedBy: .equal,
            toItem: self,
            attribute: .right, 
            multiplier: 1.0, 
            constant: 0.0).isActive = true

        let topConstraint = NSLayoutConstraint(item: customViewChild!, 
            attribute: .top, 
            relatedBy: .equal,
            toItem: self,
            attribute: .top,
            multiplier: 1.0, 
            constant: 0.0).isActive = true

        let bottomConstraint = NSLayoutConstraint(item: customViewChild!, 
            attribute: .bottom, 
            relatedBy: .equal,
            toItem: self,
            attribute: .bottom, 
            multiplier: 1.0, 
            constant: 0.0).isActive = true

        customViewChild.addConstraint([leftConstraint, rightConstraint, topConstraint, bottomConstraint]);
    }

}

Solution

  • Three things:

    1. You don't need to use addConstraint. Just set isActive to true for the constraints.
    2. It doesn't make sense to both set isActive to true and to assign the result of that to a constant. Setting the isActive property doesn't return the NSLayoutConstraint. It returns ().
    3. You should use .leading and .trailing instead of .left and .right.

    With these changes, the following should work:

    func setConstraints() {
        customViewChild = UIView()
    
        addSubview(customViewChild!)
        customViewChild?.translatesAutoresizingMaskIntoConstraints = false
    
        NSLayoutConstraint(item: customViewChild!, 
            attribute: .leading, 
            relatedBy: .equal,
            toItem: self,
            attribute: .leading, 
            multiplier: 1.0, 
            constant: 0.0).isActive = true
    
        NSLayoutConstraint(item: customViewChild!, 
            attribute: .trailing, 
            relatedBy: .equal,
            toItem: self,
            attribute: .trailing, 
            multiplier: 1.0, 
            constant: 0.0).isActive = true
    
        NSLayoutConstraint(item: customViewChild!, 
            attribute: .top, 
            relatedBy: .equal,
            toItem: self,
            attribute: .top,
            multiplier: 1.0, 
            constant: 0.0).isActive = true
    
        NSLayoutConstraint(item: customViewChild!, 
            attribute: .bottom, 
            relatedBy: .equal,
            toItem: self,
            attribute: .bottom, 
            multiplier: 1.0, 
            constant: 0.0).isActive = true
    }