Trying to show UIScrollView programmatically with Swift and not showing at all - ios

Currently I'm building a preview view programmatically due to some needed dynamic variations on UI. I already built the UI elements referenced in the code below. However, as I'm showing in the code, I've tried many things and I can't make appear the UIScrollView and of course, not even the view I'm putting inside it. I put color red to the UIScrollView to detect it and color orange to the UIView when it appears:
// SPEC DETAILS SECTION
nameLabel = UILabel();
previewButton = UIButton();
let aboutLabelCaption = UILabel();
aboutLabel = UILabel();
specsScrollView = UIScrollView();
let specsView = UIView(frame: CGRectMake(0, 0, 400, 400));
// ... Another code to setup the previews views ...
view.addSubview(specsScrollView);
var specsScrollViewConstY = NSLayoutConstraint(item: specsScrollView,
attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal,
toItem: coverImage, attribute: NSLayoutAttribute.Bottom, multiplier: 1,
constant: 10);
var specsScrollViewConstX = NSLayoutConstraint(item: specsScrollView,
attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal,
toItem: aboutLabel, attribute: NSLayoutAttribute.Trailing, multiplier: 1,
constant: 10);
var specsScrollViewConstWidth = NSLayoutConstraint(item: specsScrollView,
attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.LessThanOrEqual,
toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1,
constant: 400);
var specsScrollViewConstHeight = NSLayoutConstraint(item: specsScrollView,
attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.LessThanOrEqual,
toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1,
constant: 400);
view.addConstraint(specsScrollViewConstY);
view.addConstraint(specsScrollViewConstX);
view.addConstraint(specsScrollViewConstWidth);
view.addConstraint(specsScrollViewConstHeight);
let topLevelArray: NSArray = NSBundle.mainBundle().loadNibNamed("PreviewDetails", owner: self, options: nil);
let viewFromXib = topLevelArray.objectAtIndex(0) as UIView;
specsScrollView.backgroundColor = UIColor.redColor();
specsView.backgroundColor = UIColor.orangeColor();
specsScrollView.addSubview(specsView);
//specsScrollView.addSubview(viewFromXib);
specsScrollView.setTranslatesAutoresizingMaskIntoConstraints(false);
//specsView.setTranslatesAutoresizingMaskIntoConstraints(false);
specsScrollView.contentSize = CGSizeMake(400, 400);
var specsViewConstTop = NSLayoutConstraint(item: specsView,
attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal,
toItem: specsScrollView, attribute: NSLayoutAttribute.Top, multiplier: 1,
constant: 0);
var specsViewConstLeading = NSLayoutConstraint(item: specsView,
attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal,
toItem: specsScrollView, attribute: NSLayoutAttribute.Leading, multiplier: 1,
constant: 0);
var specsViewConstTrailing = NSLayoutConstraint(item: specsView,
attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal,
toItem: specsScrollView, attribute: NSLayoutAttribute.Trailing, multiplier: 1,
constant: 0);
var specsViewConstBottom = NSLayoutConstraint(item: specsView,
attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal,
toItem: specsScrollView, attribute: NSLayoutAttribute.Bottom, multiplier: 1,
constant: 0);
var specsViewConstHeight = NSLayoutConstraint(item: specsView,
attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal,
toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1,
constant: 400);
var specsViewConstWidth = NSLayoutConstraint(item: specsView,
attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal,
toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1,
constant: 400);
specsScrollView.addConstraint(specsViewConstTop);
specsScrollView.addConstraint(specsViewConstLeading);
specsScrollView.addConstraint(specsViewConstTrailing);
specsScrollView.addConstraint(specsViewConstBottom);
specsScrollView.addConstraint(specsViewConstHeight);
specsScrollView.addConstraint(specsViewConstWidth);
let aLabel = UILabel();
aLabel.text = "Hola mundo";
specsView.addSubview(aLabel);
// -- SPEC DETAILS SECTION
I'm not sure what am I doing badly. I expect you can help me. I want to make clear that it's not throwing any exception or error. It's just that the views I'm building programmatically are not appearing.

It would be helpful if you could post some information about the desired result (Maybe a screenshot).
Anyway, I can already see that there are some things missing in your implementation.
First of all, you need to call setTranslatesAutoresizingMaskIntoConstraints(false) on all the views you are creating programmatically, not just on the scrollView.
Secondly, when you are adding the horizontal constraints for the scrollView, you are connecting it to the aboutLabel, for which you don't add any constraints. This is the constraints I'm referring to:
var specsScrollViewConstX = NSLayoutConstraint(item: specsScrollView,
attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal,
toItem: aboutLabel, attribute: NSLayoutAttribute.Trailing, multiplier: 1,
constant: 10);
For debugging, you could call view.layoutIfNeeded() after you added the constraints, add a breakpoint after the call and then print out the frames for all the views so you can see which one is wrong.
Let me know how it goes or if you need more help.
Update
Ok, I have found the problem after running your code. It seems the way you are adding the width and height constraints is wrong for all the views.
I have tried this (I will just post the code for the scrollView, but you'll need to change for all your views):
// SPEC DETAILS SECTION
view.addSubview(specsScrollView);
var specsScrollViewConstY = NSLayoutConstraint(item: specsScrollView,
attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal,
toItem: coverImage, attribute: NSLayoutAttribute.Bottom, multiplier: 1,
constant: 10);
var specsScrollViewConstX = NSLayoutConstraint(item: specsScrollView,
attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal,
toItem: aboutLabel, attribute: NSLayoutAttribute.Trailing, multiplier: 1,
constant: 10);
var specsScrollViewConstWidth = NSLayoutConstraint.constraintsWithVisualFormat("H:[scrollview(400)]", options: nil, metrics: nil, views: ["scrollview":specsScrollView]);
var specsScrollViewConstHeight = NSLayoutConstraint.constraintsWithVisualFormat("V:[scrollview(400)]", options: nil, metrics: nil, views: ["scrollview":specsScrollView]);
view.addConstraint(specsScrollViewConstY);
view.addConstraint(specsScrollViewConstX);
view.addConstraints(specsScrollViewConstWidth);
view.addConstraints(specsScrollViewConstHeight);
This will make the scrollView show up. Once you will make these changes all your views will have the right frames, but I am not really sure that's the result you want but you can change the values to fit.
After you change all the constraints, you could delete all the statements where you set the frame for the views, it won't be necessary anymore.
Also, add an view.layoutIfNeeded() after you set all constraints, so you see the changes.
One more thing, because you're constraints were crashing when I ran the code, you need to add this for the button previewButton.setTranslatesAutoresizingMaskIntoConstraints(false)

Related

How to add constrains to storyboard for IBDesignable custom UI

I've created IBDesignable custom UIButton, this is my button:
and then I add constraints to it:
self.translatesAutoresizingMaskIntoConstraints = false
let leftConstrain = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.leadingMargin, multiplier: 1.0, constant: 8)
let rightConstrain = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.trailingMargin, multiplier: 1.0, constant: -8)
let bottomConstrain = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.bottomMargin, multiplier: 1.0, constant: -8)
let height = NSLayoutConstraint.init(item: self, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1.0, constant: 60)
NSLayoutConstraint.activate([leftConstrain, rightConstrain, bottomConstrain, height])
after I add constraints it goes to be something like this:
The problem is the frame of the button, the view height and position was changed, but the frame doesn't change and there are no constraints.
How can I force the frame to be compatible with the constraints I added?

Programatic Autolayout UI Elements

I am trying to autolayout 3 UI elements in the order which I present in the image. A UITextfield, UIDatePicker and a UIButton in a UIView.
I am avoiding to use storyboard as I want to get a better understanding of programmatic constraints and eventually use animations for them.
So far I have got this with some constraints I have tried:
and here is the code for the one I am working on:
override func viewDidLoad() {
super.viewDidLoad()
picker.translatesAutoresizingMaskIntoConstraints = false
picker.backgroundColor = UIColor.red
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blue
button.setTitle("Button", for: .normal)
self.view.addSubview(picker)
self.view.addSubview(button)
let PickercenterX = NSLayoutConstraint(item: self.picker, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let PickercenterBottom = NSLayoutConstraint(item: self.picker, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.button, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: -30)
let Pickerheight = NSLayoutConstraint(item: self.picker, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 150)
let Pickerwidth = NSLayoutConstraint(item: self.picker, attribute: NSLayoutAttribute.width, relatedBy: .equal, toItem: self.view, attribute: NSLayoutAttribute.width, multiplier: 1, constant: -5)
// Centers it on the x axis. Pushes it it right if co constant has a value > 0
let centerX = NSLayoutConstraint(item: self.button, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let centerBottom = NSLayoutConstraint(item: self.button, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: -15)
let height = NSLayoutConstraint(item: self.button, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)
let width = NSLayoutConstraint(item: self.button, attribute: NSLayoutAttribute.width, relatedBy: .equal, toItem: self.view, attribute: NSLayoutAttribute.width, multiplier: 1, constant: -15)
self.view.addConstraints([centerX, centerBottom, height, width, PickercenterX, PickercenterBottom, Pickerheight, Pickerwidth])
}
I am trying to work the button and date picker first before moving onto the textfield. How can I achieve this programmatically ?
Here lies the problem:-
You were setting the picker bottom to be equal to the button bottom with constant -30, although I know what were you trying to do, you were trying to give vertical space between picker and button. So it should be linked like, picker's bottom equal to button's top with constant -30.
Moreover you are missing out on activating the constraints by not adding isActive in the end.
Another way to activate all constraints at once is by using NSLayoutConstraint.activate() method
let PickercenterBottom = NSLayoutConstraint(item: self.picker, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.button, attribute: NSLayoutAttribute.top, multiplier: 1, constant: -30).isActive = true

Updating a Constraint Multiple Times

In my Swift app, I need to update an AutoLayout constraint multiple times, so I have a function which does this. I'm having an issue with this constraint not being updated sometimes, and I've narrowed down the issue to being that it updates the first time the function is called, but not if that function is called again.
The code from the function looks like this:
let verticalSpace = NSLayoutConstraint(item: self.prompt, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 10)
NSLayoutConstraint.activate([verticalSpace])
Does anyone know what could be causing this? Is there something which needs to be done in order to update a function multiple times?
You cannot have "competing" constraints. If you set the verticalSpace to 10, then set another verticalSpace to 20, which constraint should be used?
What you need to do is remove the existing constraint (or deactivate it), and then add/activate your new constraint.
Or...
Create your verticalConstraint and save a reference to it... then when you want to change it you can change the .constant however you wish.
Try this:
let verticalSpace = NSLayoutConstraint(item: self.prompt, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 10)
NSLayoutConstraint.activate([verticalSpace])
view.layoutIfNeeded()
Edit:
I've just achieved this by the following rule.
Either
Step 1:
remove that constraint which you want to update.
Step 2:
Add constraint again.
or
Update constraint value.
In both cases you should have the reference of that constraint which you want to update.
How:
I have just added my demo code.I have programmatically added a view and and changed it's vertical constraint by button click.Just go through the code.
Upto viewDidLoad method:
import UIKit
class ViewController: UIViewController {
let someView = UIView()
var topValue:CGFloat = 10
var verticalConstraint:NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
//This below codes are used to add a red color UIView in center which has width and height both 100
someView.backgroundColor = UIColor.red
someView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(someView)
let horizontalConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0) //Center horizontally
verticalConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: topValue) // center vertically.want to change that constraint later so took a variable.
let widthConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100) //width 100
let heightConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100) //height 100
view.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
And for each button click topConstant will be gradually updated.
This is the code for Either part which i mentioned.
self.view.removeConstraint(verticalConstraint)
verticalConstraint = NSLayoutConstraint(item: someView, attribute:
NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem:
view, attribute: NSLayoutAttribute.top, multiplier: 1, constant:
topValue)
self.view.addConstraint(verticalConstraint)
And this is the code for or part.
verticalConstraint.constant = topValue
And my buttonClick event method is basically like this.
#IBAction func updateView(_ sender: Any) {
topValue += 10
//In this case I've removed the previous constraint and add that constraint again with new Value
self.view.removeConstraint(verticalConstraint)
verticalConstraint = NSLayoutConstraint(item: someView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: topValue)
self.view.addConstraint(verticalConstraint)
//In this case I've just update that constraint value.Commented because we are using first method
//verticalConstraint.constant = topValue
}
And my output.

Put a view at the end of UIScrollview - Swift

My aim is to set a UIView at the end (right bottom) of a UIScrollView. I saw solution using UIContentSize but I don't want to use this property because it is not adapted : The UIView width isn't equal to the UIScrollview contentSize.
The UIView is a subview of the UIScrollView and I only need to scroll horizontally. I think I must use constraints in order to stick my UIView at the end of the UIscrollview (that means the UIView Right side is the same that the UIScrollView right side, both have the same height)
What I did is :
let horizontalConstraint = NSLayoutConstraint(item: self.myView , attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal, toItem: self.myScrollView , attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0)
self.myScrollView.addConstraint(horizontalConstraint)
let verticalConstraint = NSLayoutConstraint(item: self.myView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.myScrollView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
self.myScrollView.addConstraint(verticalConstraint)
I'm a bit confused and stuck about how to set this constraints, I'm probably wrong because my view doesn't display anymore. Do I need to define all the constraint or only the one for the right and one for the top ? Any advice ? Thanks
EDIT :
The solution I want could be represented like this :
UIScrollView takes it's content size from it's subviews, so it will only be as large as what it contains. We can't set it's size independently. Everything should be in a subview.
Decide on the size of myScrollView you want:
let myViewWidth = myView.frame.width
let myViewHeight = myView.frame.height
let screenWidth = myScrollView.frame.width
let scrollContentWidth = myViewWidth + screenWidth
Create a subview, let's call it widthView
let widthView = UIView(frame: CGFloat(x: 0, y: 0, width: scrollContentWidth, height: myViewHeight))
myScrollView.addSubview(widthView)
let leadingEdgeConstraint = NSLayoutConstraint(item: widthView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: myScrollView, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
myScrollView.addConstraint(leadingEdgeConstraint)
let trailingEdgeConstraint = NSLayoutConstraint(item: widthView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: myScrollView, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0)
myScrollView.addConstraint(trailingEdgeConstraint)
let widthContainerConstraint = NSLayoutConstraint(item: widthView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: scrollContentWidth)
myScrollView.addConstraint(widthContainerConstraint)
Now add your myView to that view:
widthView.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: myView , attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal, toItem: widthView , attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0)
widthView.addConstraint(horizontalConstraint)
let heightConstraint = NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: myViewHeight)
widthView.addConstraint(heightConstraint)
let widthConstraint = NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: myViewWidth)
widthView.addConstraint(widthConstraint)
Thanks to informations provided by #Tim I finally managed this without using another Subview :
Solution :
// Create a view
myView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: self.myScrollView.frame.height))
myView.backgroundColor = UIColor.redColor()
self.myScrollView.addSubview(myView)
// constraints
myView.translatesAutoresizingMaskIntoConstraints = false
let trailingSpaceConstraint = NSLayoutConstraint(item: self.myScrollView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: myView, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0)
myScrollView.addConstraint(trailingSpaceConstraint)
let screenWidth = self.view.frame.width
let leadingSpaceConstraint = NSLayoutConstraint(item: self.myView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self.myScrollView, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: screenWidth - 15)
myScrollView.addConstraint(leadingSpaceConstraint)
let widthConstraint = NSLayoutConstraint(item: self.myView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: 100)
myScrollView.addConstraint(widthConstraint)
let bottomConstraint = NSLayoutConstraint(item: self.myScrollView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.myView, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
myScrollView.addConstraint(bottomConstraint)
let topConstraint = NSLayoutConstraint(item: self.myView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.myScrollView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
myScrollView.addConstraint(topConstraint)
let heightConstraint = NSLayoutConstraint(item: self.myView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self.myScrollView, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 0)
myScrollView.addConstraint(heightConstraint)

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:
When i add Constraints in Designer:
and when i run the App it works
my Question: What is my mistake?
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.
Generate Button in Code:
Create an Constraint in Interface Builder and remove them at build time by selecting constraint in Interface Builder and check "Remove at build time"
Remove auto generated Constraints in Code
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
If you want the button at the bottom, where you are setting the constraint, remove the 100 constant.
let b = NSLayoutConstraint(item: self.button, attribute: .Bottom,
relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
If you are targeting iOS 9 you can also add your constraints like this:
button.addConstraint(button.heightAnchor.constraintEqualToConstant(80))
self.view.addConstraint(self.view.leadingAnchor.constraintEqualToAnchor(button.leadingAnchor))
self.view.addConstraint(self.view.trailingAnchor.constraintEqualToAnchor(button.trailingAnchor))
self.view.addConstraint(self.view.bottomAnchor.constraintEqualToAnchor(button.bottomAnchor))

Resources