Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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]);
    }

}
like image 386
user3832583 Avatar asked Dec 13 '22 23:12

user3832583


2 Answers

You may find this a bit easier, and more readable...

func setConstraints() {

    if customViewChild == nil {
        customViewChild = UIView()

        addSubview(customViewChild!)
        customViewChild?.translatesAutoresizingMaskIntoConstraints = false

        customViewChild?.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
        customViewChild?.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        customViewChild?.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
        customViewChild?.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
    }

}
like image 96
DonMag Avatar answered Dec 16 '22 12:12

DonMag


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
}
like image 30
vacawama Avatar answered Dec 16 '22 14:12

vacawama