user3678923 user3678923 - 1 month ago 9
iOS Question

Swift - Add NSLayoutConstraint to my Button

I want to have a button always in bottom and in left and right corner.
I want to do this with adding constraints to my button programatically.
My Code:

class LoginController: UIViewController {

@IBOutlet weak var LoginButton: UIButton!

override func loadView() {

super.loadView()

//Button Height Constraint
let constraintButtonPlayWidth = NSLayoutConstraint (item: self.LoginButton,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1,
constant: 80)
self.view.addConstraint(constraintButtonPlayWidth)

//Button Right Constraint
let r = NSLayoutConstraint(item: self.LoginButton, attribute: .Right,
relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1.0, constant: 0.0)

//Button Left Constraint
let l = NSLayoutConstraint(item: self.LoginButton, attribute: .Left,
relatedBy: .Equal, toItem: self.view, attribute: .Left, multiplier: 1.0, constant: 100.0)

//Button Bottom Constraint
let b = NSLayoutConstraint(item: self.LoginButton, attribute: .Bottom,
relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 100.0)

self.view.addConstraints([l,b,r])


When I run this code:
Run my App




When i add Constraints in Designer:
add in Designer

and when i run the App it works

my Question: What is my mistake?

Answer

If you have a view in a storyboard with no constraints, the Interface Builder will generate some constraints automatically. These auto generated constraints will conflict with the code generated ones.

So, in my opinion you have four choices.

  1. Generate Button in Code:

  2. Create an Constraint in Interface Builder and remove them at build time by selecting constraint in Interface Builder and check "Remove at build time"

  3. Remove auto generated Constraints in Code

  4. Create Constraints in Interface Builder

Option 1 - Generate Button in Code

override func viewDidLoad()
{
    super.viewDidLoad()
    let button = UIButton()
    button.setTitle("Login", forState: UIControlState.Normal)
    button.backgroundColor = UIColor.redColor()
    button.translatesAutoresizingMaskIntoConstraints = false

    self.view.addSubview(button)

    let heightConstraint = NSLayoutConstraint(
        item: button,
        attribute: NSLayoutAttribute.Height,
        relatedBy: NSLayoutRelation.Equal,
        toItem: nil,
        attribute: NSLayoutAttribute.NotAnAttribute,
        multiplier: 1.0,
        constant: 80
    )
    button.addConstraint(heightConstraint)

    let leftConstraint = NSLayoutConstraint(
        item: button,
        attribute: NSLayoutAttribute.Leading,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Leading,
        multiplier: 1.0,
        constant: 0
    )
    self.view.addConstraint(leftConstraint)

    let rightConstraint = NSLayoutConstraint(
        item: button,
        attribute: NSLayoutAttribute.Trailing,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Trailing,
        multiplier: 1.0,
        constant: 0
    )
    self.view.addConstraint(rightConstraint)

    let topConstraint = NSLayoutConstraint(
        item: button,
        attribute: NSLayoutAttribute.Bottom,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Bottom,
        multiplier: 1,
        constant: 0
    )
    self.view.addConstraint(topConstraint)
}

Option 2 - Constraints in Code (with Constraint in IB removed at build time)

Create at least one constraint (e.g. a height constraint on the login button) and set the Remove at build time checkbox. After that you will get storyboard errors, you can fix them by adding more constraints (but i think it is not really necessary to fix the storyboard errors)

override func viewDidLoad()
{
    super.viewDidLoad()

    self.LoginButton.translatesAutoresizingMaskIntoConstraints = false

    let heightConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Height,
        relatedBy: NSLayoutRelation.Equal,
        toItem: nil,
        attribute: NSLayoutAttribute.NotAnAttribute,
        multiplier: 1.0,
        constant: 80
    )
    self.LoginButton.addConstraint(heightConstraint)

    let leftConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Leading,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Leading,
        multiplier: 1.0,
        constant: 0
    )
    self.view.addConstraint(leftConstraint)

    let rightConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Trailing,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Trailing,
        multiplier: 1.0,
        constant: 0
    )
    self.view.addConstraint(rightConstraint)

    let topConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Bottom,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Bottom,
        multiplier: 1,
        constant: 0
    )
    self.view.addConstraint(topConstraint)
}

Option 3 - Remove auto generated Constraints in Code

override func viewDidLoad()
{
    super.viewDidLoad()

    var removeConstraints : [NSLayoutConstraint] = []
    for constraint in self.view.constraints
    {
        if constraint.firstItem === self.LoginButton
        {
            removeConstraints.append(constraint)
        }
    }

    self.view.removeConstraints(removeConstraints)

    self.LoginButton.removeConstraints(self.LoginButton.constraints)
    self.LoginButton.translatesAutoresizingMaskIntoConstraints = false

    let heightConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Height,
        relatedBy: NSLayoutRelation.Equal,
        toItem: nil,
        attribute: NSLayoutAttribute.NotAnAttribute,
        multiplier: 1.0,
        constant: 80
    )
    self.LoginButton.addConstraint(heightConstraint)

    let leftConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Leading,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Leading,
        multiplier: 1.0,
        constant: 0
    )
    self.view.addConstraint(leftConstraint)

    let rightConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Trailing,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Trailing,
        multiplier: 1.0,
        constant: 0
    )
    self.view.addConstraint(rightConstraint)

    let topConstraint = NSLayoutConstraint(
        item: self.LoginButton,
        attribute: NSLayoutAttribute.Bottom,
        relatedBy: NSLayoutRelation.Equal,
        toItem: self.view,
        attribute: NSLayoutAttribute.Bottom,
        multiplier: 1,
        constant: 0
    )
    self.view.addConstraint(topConstraint)
}

Option 4 - Create in Interface Builder

Create Constraints in Interface Builder