How to set NSLayoutConstraint in Swift?
Insert in cellForRowAt
in the vc
cell.updateCellView(arr[indexPath.row])//// change arr to your datasource arr inside here set constants
cell.layoutIfNeeded()
return cell
at the end of the if statements
Difference between using NSLayoutConstraints initializer compared to Anchors for setting constraints
In large part, it is new syntax and readability, but there are still some things that you can do with NSLayoutConstraint(...)
that you cannot do the "new" way.
For example, let's take a simple task of adding a UILabel
centered horizontally, 40-pts from the bottom. Each of the following examples will begin with this:
override func viewDidLoad() {
super.viewDidLoad()
let myView = UILabel()
myView.backgroundColor = .green
myView.text = "Hello"
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
// add constraints....
}
So, our first method looks like this:
// center horizontally
NSLayoutConstraint(item: myView,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1.0,
constant: 0.0).isActive = true
// bottom = 40-pts from view Bottom
NSLayoutConstraint(item: myView,
attribute: .bottom,
relatedBy: .equal,
toItem: view,
attribute: .bottom,
multiplier: 1.0,
constant: -40.0).isActive = true
We can get the exact same results using this syntax, which would generally be considered more "readable":
// center horizontally
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// bottom = 40-pts from view Bottom
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -40.0).isActive = true
Now, we'll usually have many more constraints to set, so we can make it even more readable with this (eliminates the .isActive = true
at the end of every line):
NSLayoutConstraint.activate([
// center horizontally
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// bottom = 40-pts from view Bottom
myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -40.0),
])
So... What happens if we throw in just a little complexity, such as saying "Keep the Bottom of the Label 20% from the bottom of the view"?
To stick with the "new, more readable" syntax, we have a couple options...
1 - constrain the bottom of the Label to the bottom of the view, wait until layout is finished - so we know the height of the view - and then set the .constant
on the bottom anchor to -(view height * 0.2)
. That will work, but we have to re-calculate every time the label's superview changes (such as on device rotation).
2 - add a UIView
as a "bottom spacer":
// add a hidden UIView for bottom "space"
let spacerView = UIView()
spacerView.isHidden = true
view.addSubview(spacerView)
spacerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// spacerView at bottom, height = 20% of view height
spacerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0),
spacerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
// center horizontally
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// bottom = spacerView Top
myView.bottomAnchor.constraint(equalTo: spacerView.topAnchor, constant: 0.0),
])
That works, and handles superview size changes, but we've added another view to the view hierarchy. For this simple example, no big deal, but we probably don't want to add a bunch of them for a complex layout.
3 - add a UILayoutGuide
as a "bottom spacer":
// add a UILayoutGuide for bottom "space"
let spacerGuide = UILayoutGuide()
view.addLayoutGuide(spacerGuide)
NSLayoutConstraint.activate([
// spacerGuide at bottom, height = 20% of view height
spacerGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0),
spacerGuide.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
// center horizontally
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
// bottom = spacerGuide Top
myView.bottomAnchor.constraint(equalTo: spacerGuide.topAnchor, constant: 0.0),
])
Accomplishes the same thing, but now we're using a non-rendering UI element so we're not weighing down the view hierarchy.
4 - Use the NSLayoutConstraint(...)
syntax, and avoid all of that:
// center horizontally
NSLayoutConstraint(item: myView,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1.0,
constant: 0.0).isActive = true
// bottom = 80% of view bottom (leaves 20% space at bottom)
NSLayoutConstraint(item: myView,
attribute: .bottom,
relatedBy: .equal,
toItem: view,
attribute: .bottom,
multiplier: 0.8,
constant: 0.0).isActive = true
}
So, for most situations, it is a matter of preference and/or consistency, but you will find the occasional case where there is a difference.
How to set variations of a NSLayoutConstraint constant in code?
To do this programmatically you override traitCollectionDidChange:
method in your view controller.
You can then look at self.traitCollection.horizontalSizeClass
and self.traitCollection.verticalSizeClass
to decide what to do. Use the reference that you have created to the layout constraint to set the constant accordingly.
After all of your layout constraints are set, call updateConstraints on your view to trigger a layout pass.
For example:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
let hCompact = (self.traitCollection.horizontalSizeClass == .compact)
let vRegular = (self.traitCollection.verticalSizeClass == .regular)
if hCompact && vRegular {
self.betweenTextTerms.constant = 45
}
self.updateViewConstraints()
}
For simple cases, it is much more convenient to do this in the Storyboard!
NSLayoutConstraint - Two items, one line, spacing priority
Giving the left label a higher content hugging priority should do the trick.
// In code
leftLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
For further reading on that topic, I found this to be pretty helpful
Playground example
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let leftLabel = UILabel()
leftLabel.text = "I am left"
leftLabel.backgroundColor = .orange
leftLabel.translatesAutoresizingMaskIntoConstraints = false
let rightLabel = UILabel()
rightLabel.text = "I am right"
rightLabel.backgroundColor = .yellow
rightLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(leftLabel)
view.addSubview(rightLabel)
leftLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
leftLabel.trailingAnchor.constraint(equalTo: rightLabel.leadingAnchor).isActive = true
leftLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 16).isActive = true
// This is the important part
leftLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
rightLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
rightLabel.topAnchor.constraint(equalTo: leftLabel.topAnchor).isActive = true
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
iOS adding constraints programmatically in Swift
You should add constraints only after view is added to the view hierarchy. From your code it is clear that you have not added the UILabel instance to view.
Related Topics
Most Efficient/Realtime Way to Get Pixel Values from iOS Camera Feed in Swift
How to Create a Pfquerytableviewcontroller with Parse in Swift
Convenience Initialization of Uinavigationcontroller Subclass Makes Subclass Constant Init Twice
Today Extension with Uicollectionview Different Behaviour Compared to Single View Application
Avplayer Not Full Screen in Landscape Mode Using iOS Swift
Swift Sending Data from Child View to Parent View Controller
Swift: Load Custom Uiview from Xib Programmatically
Daily Local Notifications Are Not Working
Create Navbar Programmatically with Button and Title Swift
iOS Swift3 Check Nil Value for Viewcontroller Object
Setup a Collectionview with "Tag"-Like Cells
How to Make a Bullet List with Swift
Disable Vertical Scroll in Uiscrollview Swift
Settitletextattributes Doesn't Work for Uitabbaritem When It Is Unselected in Swift
Skspritenode Does Not Let Touches Pass Through When Isuserinteractionenabled False