Nslayoutconstraint in Swift

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)

left label content hugging priority

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



Leave a reply



Submit