- Learning Objectives
- Constraints programmatically
- Anchors and Layout Constraints
By the end of this lesson, students should be able to:
- Use the
NSLayoutConstraint
andNSLayoutAnchor
classes - Set up a scroll view using only code
- Create an onboarding flow with AutoLayout programmatically
Layout anchors let us create constraints that are easy to read and in a compact format.
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
Sample view
import PlaygroundSupport
import UIKit
class ViewController: UIViewController {
var exampleView: UIView!
override func loadView() {
super.loadView()
self.view.backgroundColor = UIColor.white
exampleView = UIView(frame: .zero)
exampleView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(exampleView)
exampleView.widthAnchor.constraint(equalToConstant: 80).isActive = true
exampleView.heightAnchor.constraint(equalTo: exampleView.widthAnchor, multiplier: 1).isActive = true
exampleView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
exampleView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
self.exampleView.backgroundColor = .purple
}
}
PlaygroundPage.current.liveView = ViewController()
In this example we have several common constraints we will need to use at some point.
- Setting a fixed width or height
- Setting an aspect ratio
- Centering horizontally and vertically
Another common constraint that is not shown is setting a proportional width/height.
exampleView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.5)
If you noticed in the code snippet, we gave the view a frame with a zero rect.
This is just saying that the rectangle will have its origin and size set to zero.
CGRect(x: 0, y: 0, width: 0, height: 0)
When using AutoLayout we need this property to be set to false.
If you're using the interface builder, this is done for you.
If you are setting constraints programmatically, you need to remember setting this to false or you'll get an error message of conflicting constraints.
If you want to read more on why this conflicting error happens, you can find a very clear explanation here.
To activate them all 😎
NSLayoutConstraint.activate([
...
])
- As much as you can, never calculate frames manually
- Set
translatesAutoresizingMaskIntoConstraints
to false - Add the view to the hierarchy with
addSubview
- Activate your constraints
Using the anchors approach, code the following:
- Square 2 is 50 pts below the square 1 - Square 2 is twice the size of square 1 - Square 2 to the right of square 1 <iframe src="https://www.youtube.com/embed/fQlEaJxHfcc" data-autoplay width="700" height="500"></iframe>This method explicitly converts the constraint equation into code. Each parameter corresponds to a part of the equation.
Unlike the approach taken by the layout anchor API, you must specify a value for each parameter, even if it doesn’t affect the layout. The end result is a considerable amount of boilerplate code, which is usually harder to read.Examine how this code works and then place a new view to the right of the exampleView.
import PlaygroundSupport
import UIKit
class ViewController: UIViewController {
var exampleView: UIView!
var redView: UIView!
override func loadView() {
super.loadView()
exampleView = UIView()
exampleView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(exampleView)
redView = UIView()
redView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(redView)
NSLayoutConstraint (item: exampleView!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 80).isActive = true
NSLayoutConstraint(item: exampleView!, attribute: .height, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant:80.0).isActive = true
NSLayoutConstraint(item: exampleView!, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 60.0).isActive = true
NSLayoutConstraint(item: exampleView!, attribute: .top, relatedBy: .equal, toItem: view, attribute: .topMargin, multiplier: 1.0, constant: 20.0).isActive = true
NSLayoutConstraint (item: redView!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 80).isActive = true
NSLayoutConstraint(item: redView!, attribute: .height, relatedBy: .equal, toItem: nil, attribute:.notAnAttribute, multiplier: 1.0, constant:80.0).isActive = true
NSLayoutConstraint(item: redView!, attribute: .leading, relatedBy: .equal, toItem: exampleView, attribute: .trailing, multiplier: 1.0, constant: 20).isActive = true
NSLayoutConstraint(item: redView!, attribute: .top, relatedBy: .equal, toItem: exampleView, attribute: .top, multiplier: 1.0, constant: 0.0).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
self.exampleView.backgroundColor = .purple
self.redView.backgroundColor = .red
}
}
PlaygroundPage.current.liveView = ViewController()
Look up constraints with visual format. It's another way to add constraints to views. We won't cover it since it's very verbose and not really necessary, but you might see them in the wild, or when working in an old project.
Repeat the first in class activity using the visual format language.
Free Illustrations