NSLayoutConstraints Class
In this approach we actually create a constraints class object and the provide the details. Basically we use the NSLayoutConstraints init method to create constraints. Here is how it looks:
NSLayoutConstraint(item: list, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1.0, constant: 10.0).isActive = true
It becomes immediately clear that we have to provide all the information to create the constraints object. This has to be done when if the information is not necessary for the creation of the constraint. Let us have a look at some code to see how it works.
I will be continuing from the same UIStackView project.
- Switch to the ViewController.swift file. Add an extension to the ViewController class.
- Add the function to apply constraints to the title elements as shown below:
func apply_constraints_to_title()
{
icon.setContentCompressionResistancePriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.horizontal)
icon.setContentCompressionResistancePriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical)
appTitle.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: NSLayoutConstraint.Axis.horizontal)
let apptitleConstraint : NSLayoutConstraint = NSLayoutConstraint(item: appTitle, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: icon, attribute: NSLayoutConstraint.Attribute.width, multiplier: 4.0, constant: 0.0)
apptitleConstraint.isActive = true
}
Let us examine the line where we create the constraint.
let apptitleConstraint : NSLayoutConstraint = NSLayoutConstraint(item: appTitle, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: icon, attribute: NSLayoutConstraint.Attribute.width, multiplier: 4.0, constant: 0.0)
Here we are creating the constraint object. We are specifying all the components of the equation
3. Similarly write the following code down to complete the implementation for the remaining views.
func apply_constraints_to_fields()
{
let nameFieldConstraint : NSLayoutConstraint = NSLayoutConstraint(item: nameField, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0)
nameFieldConstraint.isActive = true
let emailFieldConstraint : NSLayoutConstraint = NSLayoutConstraint(item: emailField, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0)
emailFieldConstraint.isActive = true
let fieldConstraint : NSLayoutConstraint = NSLayoutConstraint(item: nameField, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: emailField, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 0.0)
fieldConstraint.isActive = true
}
func apply_constraints_to_age()
{
let ageConstraint : NSLayoutConstraint = NSLayoutConstraint(item: age, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: ageValue, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 0.0)
ageConstraint.isActive = true
}
func apply_constraints_to_service()
{
let serviceConstraint : NSLayoutConstraint = NSLayoutConstraint(item: serviceRating, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: serviceLbl, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.5, constant: 0.0)
serviceConstraint.isActive = true
let serviceLblConstraint : NSLayoutConstraint = NSLayoutConstraint(item: serviceLbl, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0)
serviceLblConstraint.isActive = true
serviceLbl.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: NSLayoutConstraint.Axis.horizontal)
}
func apply_constraints_to_satisfaction()
{
let satisfactionConstraint : NSLayoutConstraint = NSLayoutConstraint(item: satisfaction, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: satisfactionLbl, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.5, constant: 0.0)
satisfactionConstraint.isActive = true
let satisfactionLblCnstr : NSLayoutConstraint = NSLayoutConstraint(item: satisfactionLbl, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0)
satisfactionLblCnstr.isActive = true
satisfactionLbl.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: NSLayoutConstraint.Axis.horizontal)
}
func apply_constraints_to_buttons()
{
let btnConstraint : NSLayoutConstraint = NSLayoutConstraint(item: saveBtn, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: fetchBtn, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 0.0)
btnConstraint.isActive = true
}
func apply_constraints_to_all_Stacks()
{
NSLayoutConstraint(item: titleStack, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0).isActive = true
NSLayoutConstraint(item: ageStack, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0).isActive = true
NSLayoutConstraint(item: serviceStack, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0).isActive = true
NSLayoutConstraint(item: satisfactionStk, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0).isActive = true
NSLayoutConstraint(item: buttonStk, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1.0, constant: 100.0).isActive = true
NSLayoutConstraint(item: buttonStk, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.greaterThanOrEqual, toItem: nil, attribute: NSLayoutConstraint.Attribute.height, multiplier: 1.0, constant: 30.0).isActive = true
NSLayoutConstraint(item: serviceStack, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: satisfactionStk, attribute: NSLayoutConstraint.Attribute.height, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: satisfactionStk, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: buttonStk, attribute: NSLayoutConstraint.Attribute.height, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: ageStack, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: buttonStk, attribute: NSLayoutConstraint.Attribute.height, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: titleStack, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: ageStack, attribute: NSLayoutConstraint.Attribute.height, multiplier: 1.0, constant: 0.0).isActive = true
titleStack.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.horizontal)
agePicker.setContentHuggingPriority(UILayoutPriority.defaultLow, for: NSLayoutConstraint.Axis.vertical)
nameField.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: NSLayoutConstraint.Axis.vertical)
emailField.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: NSLayoutConstraint.Axis.vertical)
}
func apply_constraints_to_enclosing_stack()
{
NSLayoutConstraint(item: enclosingStack, attribute: NSLayoutConstraint.Attribute.leading, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.leading, multiplier: 1.0, constant: 10.0).isActive = true
NSLayoutConstraint(item: enclosingStack, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1.0, constant: -10.0).isActive = true
NSLayoutConstraint(item: enclosingStack, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1.0, constant: -30.0).isActive = true
NSLayoutConstraint(item: enclosingStack, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: 170.0).isActive = true
}
The code is the same throughout. The only change I made was to skip saving the object reference in another variable. I directly chained the property isActive to the init call.
4. Consolidate all the calls in a single function.
func apply_constraints()
{
self.apply_constraints_to_title()
self.apply_constraints_to_fields()
self.apply_constraints_to_age()
self.apply_constraints_to_service()
self.apply_constraints_to_satisfaction()
self.apply_constraints_to_buttons()
self.apply_constraints_to_all_Stacks()
self.apply_constraints_to_enclosing_stack()
}
5. Call the consolidated function in the viewDidLoad method.
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
self.navigationItem.title = "Feedback"
self.configureUIElements()
self.configureStacks()
self.apply_constraints()
}
Run the project and see how the second screen comes up. Feel free to add or remove constraints to see how the UI renders.
Here is the link to the completed project using NSLayoutConstraints.
Fixed the issue with the links to the sample projects. They should be working fine now.