ScrollView Won't Scroll AHHH - ios

For the life of me I can't figure out what I'm doing wrong. All I'm trying to do is put a bunch of text fields in a scroll view and have it intrinsically determine the contentSize using auto layout. Unlike the other tutorials I've watched and read out there, I'm trying to do this programmatically, not through Storyboards or Xibs.
I've called setTranslatesAutoresizingMaskIntoConstraints(false) on every view in the scroll view (including on the scroll view itself).
I've set the height and width of the scroll view to match it's superview and even positioned the scroll view in the center of it's superview (I read that sometimes you need to do that to get it working)
I've pinned the content view's edges to the top, leading, trailing, and bottom edges of it's superview (the scroll view)
Here's my code:
class ViewController: UIViewController {
var scrollView = UIScrollView()
var contentView = UIView()
var textField1 = CustomTextField()
var textField2 = CustomTextField()
var textField3 = CustomTextField()
var textField4 = CustomTextField()
var textField5 = CustomTextField()
var textField6 = CustomTextField()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
contentView.setTranslatesAutoresizingMaskIntoConstraints(false)
textField1.setTranslatesAutoresizingMaskIntoConstraints(false)
textField2.setTranslatesAutoresizingMaskIntoConstraints(false)
textField3.setTranslatesAutoresizingMaskIntoConstraints(false)
textField4.setTranslatesAutoresizingMaskIntoConstraints(false)
textField5.setTranslatesAutoresizingMaskIntoConstraints(false)
textField6.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(textField1)
contentView.addSubview(textField2)
contentView.addSubview(textField3)
contentView.addSubview(textField4)
contentView.addSubview(textField5)
contentView.addSubview(textField6)
contentView.backgroundColor = .redColor()
setupConstraints()
}
func setupConstraints() {
// Install height and width constraints on our scroll view
let heightLC : NSLayoutConstraint = NSLayoutConstraint(item: scrollView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0)
let widthLC : NSLayoutConstraint = NSLayoutConstraint(item: scrollView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0)
self.view.addConstraints([heightLC,widthLC])
// Install centering constraints on our scroll view
let centerXLC : NSLayoutConstraint = NSLayoutConstraint(item: scrollView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0)
let centerYLC : NSLayoutConstraint = NSLayoutConstraint(item: scrollView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0)
self.view.addConstraints([centerXLC,centerYLC])
// Set the content view so it resizes with the same proportions on screen rotations //
let contentViewTopLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0)
let contentViewLeadingLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0)
let contentViewTrailingLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0)
let contentViewBottomLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0)
let contentViewWidthLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0)
let contentViewHeightLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0)
scrollView.addConstraints([contentViewTopLC,contentViewLeadingLC,contentViewTrailingLC,contentViewBottomLC,contentViewWidthLC,contentViewHeightLC])
// Setup the text field constraints
let textField1Top : NSLayoutConstraint = NSLayoutConstraint(item: textField1, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0)
let textField1Leading : NSLayoutConstraint = NSLayoutConstraint(item: textField1, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 40)
let textField1Trailing : NSLayoutConstraint = NSLayoutConstraint(item: textField1, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: -40)
contentView.addConstraints([textField1Top, textField1Leading, textField1Trailing])
let textField2Top : NSLayoutConstraint = NSLayoutConstraint(item: textField2, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: textField1, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 100)
let textField2Leading : NSLayoutConstraint = NSLayoutConstraint(item: textField2, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 40)
let textField2Trailing : NSLayoutConstraint = NSLayoutConstraint(item: textField2, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: -40)
contentView.addConstraints([textField2Top, textField2Leading, textField2Trailing])
let textField3Top : NSLayoutConstraint = NSLayoutConstraint(item: textField3, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: textField2, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 100)
let textField3Leading : NSLayoutConstraint = NSLayoutConstraint(item: textField3, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 40)
let textField3Trailing : NSLayoutConstraint = NSLayoutConstraint(item: textField3, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: -40)
contentView.addConstraints([textField3Top, textField3Leading, textField3Trailing])
let textField4Top : NSLayoutConstraint = NSLayoutConstraint(item: textField4, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: textField3, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 100)
let textField4Leading : NSLayoutConstraint = NSLayoutConstraint(item: textField4, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 40)
let textField4Trailing : NSLayoutConstraint = NSLayoutConstraint(item: textField4, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: -40)
contentView.addConstraints([textField4Top, textField4Leading, textField4Trailing])
let textField5Top : NSLayoutConstraint = NSLayoutConstraint(item: textField5, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: textField4, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 100)
let textField5Leading : NSLayoutConstraint = NSLayoutConstraint(item: textField5, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 40)
let textField5Trailing : NSLayoutConstraint = NSLayoutConstraint(item: textField5, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: -40)
contentView.addConstraints([textField5Top, textField5Leading, textField5Trailing])
let textField6Top : NSLayoutConstraint = NSLayoutConstraint(item: textField6, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: textField5, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 100)
let textField6Leading : NSLayoutConstraint = NSLayoutConstraint(item: textField6, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 40)
let textField6Trailing : NSLayoutConstraint = NSLayoutConstraint(item: textField6, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: -40)
let textField6Bottom : NSLayoutConstraint = NSLayoutConstraint(item: textField6, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: contentView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0)
contentView.addConstraints([textField6Top, textField6Leading, textField6Trailing, textField6Bottom])
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And here's my CustomTextField class I use for the text fields:
class CustomTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .whiteColor()
// set text field height to 55pts
let heightLC : NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 55)
addConstraint(heightLC)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here's what the app currently looks like
(notice the 6th text field isn't being displayed and I can't scroll down to it):
Any help will greatly be appreciated :)

The problem is these lines:
let contentViewTopLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0)
let contentViewLeadingLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0)
let contentViewTrailingLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0)
let contentViewBottomLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0)
let contentViewWidthLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0)
let contentViewHeightLC : NSLayoutConstraint = NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0)
scrollView.addConstraints([contentViewTopLC,contentViewLeadingLC,contentViewTrailingLC,contentViewBottomLC,contentViewWidthLC,contentViewHeightLC])
What you want is not for the content view to be sized like the scroll view at all. That is exactly what is keeping the scroll view from scrolling. You want the content view's four edges to be pinned to the four edges of the scroll view.
That way, the contents of the content view will push outwards on the content view, sizing it. It will thus try to become bigger than the scroll view. But it can't, because you're also going to pin the sides of the scroll view to its superview. Thus, the content view's size will become the bounds size of the scroll view - its contentSize. When the contentSize of the scroll view is thus bigger than the scroll view, it will be scrollable.

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

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))

Swift UITextField Constraint - Add Textfield in Middle of the View

I want to add a UITextField to my View programatically.
The distance from left and from right should be 20.
The distance from top should be 100.
if i add constraint in the designer it works:
-> in Simulator it looks like that:
My code now:
let EmailTextfield = UITextField()
override func loadView() {
super.loadView()
EmailTextfield.translatesAutoresizingMaskIntoConstraints = false
EmailTextfield.layer.borderWidth = 2
EmailTextfield.layer.borderColor = UIColor.blackColor().CGColor
EmailTextfield.addConstraint(
NSLayoutConstraint(
item: EmailTextfield,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1.0,
constant: 40
))
//left
self.view.addConstraint(
NSLayoutConstraint(
item: EmailTextfield,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Leading,
multiplier: 1.0,
constant: 20
))
//right
self.view.addConstraint(
NSLayoutConstraint(
item: EmailTextfield,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Trailing,
multiplier: 1.0,
constant: 20
))
//top
self.view.addConstraint(
NSLayoutConstraint(
item: EmailTextfield,
attribute: NSLayoutAttribute.Top,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Top,
multiplier: 1.0,
constant: 100
))
)
When i run my code:
What is my mistake?
The trailing constant must be the negative value -20 instead of 20.
you can change the RIGHT constraints constant from 20 to -20. or you can switch the RIGHT constraints items (item: self.view, toItem: EmailTextfield) and leave the constant being 20.

Resources