How to add constrains to storyboard for IBDesignable custom UI - ios

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?

Related

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

UITextField autolayout with margins programmatically

I'm new to AutoLayout and would like to display my UITextField at 100% width with a consistent 15px left and right margin, like so:
Typically I would do this using CGRect, setting the width to the containing view's width minus 30px, then offset the left side by 15px:
searchTextField.frame = CGRectMake(15, 0, view.frame.width - 30, 50)
But I'd like to learn AutoLayout for this sort of thing, since it's the way to go these days. I should note that I am doing everything programmatically -- no Storyboards here.
I'd love it if someone could help me out!
Update
Wow! Thank you for all the responses. I believe all of them would achieve what I'm trying to do, but there can only be one :)
Usually I use for this cocoapod that is dealing with constraints, but if you need pure apple solution documentation states:
You have three choices when it comes to programmatically creating
constraints: You can use layout anchors, you can use the
NSLayoutConstraint class, or you can use the Visual Format Language.
Approach with NSLayoutConstraints in your case would be:
NSLayoutConstraint(item: textField, attribute: .Leading, relatedBy: .Equal, toItem: parentView, attribute: .LeadingMargin, multiplier: 1.0, constant: 15.0).active = true
NSLayoutConstraint(item: textField, attribute: .Trailing, relatedBy: .Equal, toItem: parentView, attribute: .TrailingMargin, multiplier: 1.0, constant: -15.0).active = true
NSLayoutConstraint(item: textField, attribute: .Top, relatedBy: .Equal, toItem: parentView, attribute: .TopMargin, multiplier: 1.0, constant: 50.0).active = true
Remember that if you don't have any constraints in the view, they would be added automatically and you'll have to deal with them and conflicts that would be created by adding new constraints on runtime. To avoid this you could either create textField manually and add it to the view or set constraints with low priority in the Interface Builder .
Assuming the parent of the text field is view, do this after view.addSubview(searchTextField):
NSLayoutConstraint.activateConstraints([
searchTextField.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 15),
searchTextField.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor, constant: -15),
])
Use this code:
let topConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Top, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Trailing, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 15)
let leadingConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Leading, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 15)
let heightConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Height, relatedBy: .Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 50)
self.view.addConstraint(topConstraint )
self.view.addConstraint(trailingConstraint)
self.view.addConstraint(leadingConstraint)
self.view.addConstraint(heightConstraint)
Set the constraints in storyboard.
Click on the text field then click on in the bottom left. From there you can choose constraints like that.
To use Auto Layout, you need to define constraints for your text field.Here, I have created four constraints(Leading, Trailing, Top and Height) related to its superview.
func addLabelConstraints(superView:UIView) {
let leading = NSLayoutConstraint(item: searchTextField, attribute: .Leading, relatedBy: .Equal, toItem: superView, attribute: .Leading, multiplier: 1, constant: 15)
superview!.addConstraint(leading)
let trailing = NSLayoutConstraint(item: searchTextField, attribute: .Trailing, relatedBy: .Equal, toItem: superView, attribute: .Trailing, multiplier: 1, constant: 15)
superView.addConstraint(trailing)
let top = NSLayoutConstraint(item: searchTextField, attribute: .Top, relatedBy: .Equal, toItem: superView, attribute: .Top, multiplier: 1, constant: 0)
superView.addConstraint(top)
let height = NSLayoutConstraint(item: searchTextField, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 0, constant: 50)
superView.addConstraint(height)
}

Constraints are not expanding my View on orientation change

I created my view as so :
self.scrollView = UIScrollView()
self.scrollView.delegate = self
self.scrollView.contentSize = CGSizeMake(UIScreen.mainScreen().bounds.width, 1000)
containerView = UIView()
containerView.backgroundColor = UIColor.redColor()
scrollView.translatesAutoresizingMaskIntoConstraints = false
containerView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(containerView)
view.addSubview(scrollView)
And then I added these constraints :
// Constraint ScrollView
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1.0, constant: 0.0))
// Constraint ContainerView
scrollView.addConstraint(NSLayoutConstraint(item: containerView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0.0))
scrollView.addConstraint(NSLayoutConstraint(item: containerView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0.0))
scrollView.addConstraint(NSLayoutConstraint(item: containerView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0.0))
scrollView.addConstraint(NSLayoutConstraint(item: containerView, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 0.0))
And this :
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
containerView.frame = CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height)
}
But when I change the orientation, the width of the containerView remains the same. How can I ensure that the width expands to the width of the scrollView to it's new layout?
You should replace
scrollView.addConstraint(NSLayoutConstraint(item: containerView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0.0))
scrollView.addConstraint(NSLayoutConstraint(item: containerView, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 0.0))
with
view.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Height, relatedBy: .Equal, toItem: scrollView, attribute: .Height, multiplier: 1.0, constant: 0.0))
view.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Width, relatedBy: .Equal, toItem: scrollView, attribute: .Width, multiplier: 1.0, constant: 0.0))
This uses the width and height of the scroll view instead of stretching the container view to the right side and bottom of the scroll view. It seems to work much better. I also added the constraint to a common ancestor, as per the docs but it also works by adding to scrollView, so take your choice whether you want to add all constraints to the view instead of the scrollView or not.
Further note
In iOS 8 and later you can simply activate your constraints instead of adding them to your view.
NSLayoutConstraint(item: containerView, attribute: .Top, relatedBy: .Equal, toItem: scrollView, attribute: .Top, multiplier: 1.0, constant: 0.0).active = true
In iOS 9, which I like even more you can use anchors, e.g.
scrollView.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor).active = true
You don't need to do anything in layoutSubviews. The translatesAutoresizingMaskIntoConstraints property should indeed be set to false NOT true.
you should also add these constraints
containerView.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Top, relatedBy: .Equal, toItem: scrollView, attribute: .Top, multiplier: 1.0, constant: 0.0))
containerView.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Bottom, relatedBy: .Equal, toItem: scrollView, attribute: .Bottom, multiplier: 1.0, constant: 0.0))
containerView.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Leading, relatedBy: .Equal, toItem: scrollView, attribute: .Leading, multiplier: 1.0, constant: 0.0))
containerView.addConstraint(NSLayoutConstraint(item: containerView, attribute: .Trailing, relatedBy: .Equal, toItem: scrollView, attribute: .Trailing, multiplier: 1.0, constant: 0.0))

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

ScrollView Won't Scroll AHHH

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.

Resources