How To Change Swift Constraints To Make Autolayout Programmatically

In the previous article, we have learned how to change swift constraints use the auto-layout toolbar visually ( How To Add Constraints In Xcode 10 To Implement Auto Layout ). In this article, I will show you an example of how to use class UIKit.NSLayoutConstraint, UIKit.NSLayoutAnchor and Visual Format Language to add constraints to the UIView object programmatically.

Please note, no matter which method you use to add constraints to the UIView object, you should first set the UIView object’s translatesAutoresizingMaskIntoConstraints property’s value to false, otherwise, when you change constraints to the UIView object, it will throw an error message like [LayoutConstraints] Unable to simultaneously satisfy constraints.

1. Add Constraints To UIView Object Use NSLayoutConstraint Class.

Invoke NSLayoutConstraint class’s init method to create constraint object. Do not forget to activate the constraint object after create it. You can read more at the apple NSLayoutConstraint init method document.

// Set property translatesAutoresizingMaskIntoConstraints to false to avoid [LayoutConstraints] Unable to simultaneously satisfy constraints error.
backgroundView.translatesAutoresizingMaskIntoConstraints = false

// Initialize a NSLayoutConstraint object.
let topConstraint = NSLayoutConstraint.init(item: backgroundView, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: backgroundViewMargin)
        
// Activate top constraint to enable it.
topConstraint.isActive = true



// Below code will make the label object's horizontal layout at the screen center use NSLayoutConstraint. 

// Initialize a NSLayoutConstraint object, and make it layout at the center of the screen horizontal direction.
let centerConstraint = NSLayoutConstraint.init(item: label, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: 

NSLayoutConstraint.Attribute.centerX, multiplier: 1.0, constant: 0)

// Active center constraint.
centerConstraint.isActive = true

2. Add Constraints To UIView Object Use NSLayoutAnchor Class.

NSLayoutAnchor class make add constraint more simple with less code. Each UIView object has topAnchor, leftAnchor, widthAnchor, and heightAnchor …… attributes which is an instance of NSLayoutAnchor class’s subclass. You can call the NSLayoutAnchor class’s constraint method to add a constraint. Do not forget to make the constraint object active after creating it. You can read more at the NSLayoutAnchor class document.

// Set source object's property translatesAutoresizingMaskIntoConstraints to false to avoid [LayoutConstraints] Unable to simultaneously satisfy constraints error.
redView.translatesAutoresizingMaskIntoConstraints = false

// Get top NSLayoutConstraint object.
let topConstraint = redView.topAnchor.constraint(equalTo: backgroundView.topAnchor, constant: redViewMargin)

// Activate above constraint object to make it effective.
topConstraint.isActive = true

// Below code will make the label object's horizontal layout at the screen center use NSLayoutAnchor.

let centerXAnchor = label.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
centerXAnchor.isActive = true

3. Add Constraints To UIView Object Use Visual Format Language.

Besides the above two methods, you can also use Visual Format Language to add constraints to the UIView object. Visual format language is just a text string that defines the constraints layout meanings. For example “H:|-100-[greenView(200)]” means the greenView‘s width is 200 pt, and the distance between it’s left edge to it’s parent view left edge is 100 pt. You can read the apple Visual Format Language document for a detailed explanation.

Below is the source code about how to use swift visual format language to add constraints to the UIView object.

// First define a horizontal visual format language text string. Please note the string left and greenView is the placeholder string, they will be replaced in later code. The greenView's width is 100pt.

let horizontalVFL = "H:|-left-[greenView(100)]"

// Define a vertical visual format language text string. The string top and greenView is also placeholder string. The greenView's height is 200pt.
let verticalVFL = "V:|-top-[greenView(200)]"

// Define a dictionary object, the key is the direction space constraint placeholder string in above VFL string. The value is space distance value in that direction.
let metrics = ["left":100, "top":100]

// Define a dictionary object, the key is the view name placeholder string in above VFL string. The value is the first view item.
let views = ["greenView": greenView]

// Create horizontal constraint use horizontal VFL string with passed dictionary objects.
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: horizontalVFL, options: NSLayoutConstraint.FormatOptions.alignAllLeading, metrics: metrics, views: views)

// Create vertical constraint use vertical VFL string with passed dictionary objects.
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: verticalVFL, options: NSLayoutConstraint.FormatOptions.alignAllLeading, metrics: metrics, views: views)

// Add above horizontal and vertical constraints to parent view to enable constraints.
parentView.addConstraints(horizontalConstraints)
parentView.addConstraints(verticalConstraints)

4. Add Constraints Programmatically In Swift Example.

There is three UIView objects in this example, a gray view, a red view, and a green view. The red view and green view are gray view’s subview. The gray view’s constraints are added use NSLayoutConstraint class’s init method, the red view’s constraints is added use NSLayoutAnchor class’s constraint method, the green view’s constraints is added use Visual Format Language. When you change the screen orientation, the red view and green view will change it’s size to match the screen change automatically.

If you can not watch the above video, you can see it on the youtube URL https://youtu.be/XnRFkeKLH70.

5. Add Swift Constraints Programmatically Source Code.

After you create an Xcode project, edit ViewController.swift file uses the below source code.  You will need to override the viewWillTransition function, this function is triggered when the screen orientation is changed, so we exchange the screen width and height value in that function.

//
//  ViewController.swift
//  SwiftMakeConstraintsProgrammaticallyExample
//
//  Created by song zhao on 7/24/19.
//  Copyright © 2019 dev2qa.com. All rights reserved.
//

import UIKit.NSLayoutAnchor

class ViewController: UIViewController {
    
    // Get iOS device screen width and height.
    var screenWidth = UIScreen.main.bounds.width
    var screenHeight = UIScreen.main.bounds.height
    
    // Create background view object, other views will be added to this view.
    var backgroundView = UIView()
    let backgroundViewMargin = CGFloat(20)
    
    // Create a red view object.
    var redView = UIView()
    // redViewMargin is relative to it's parent view ( backgroundView ).
    let redViewMargin = CGFloat(10)
    let redViewWidth = CGFloat(300)
    let redViewHeight = CGFloat(200)
    
    // Create a green view object.
    var greenView = UIView()
    // greenViewMarginLeft is also relative to it's parent view ( backgroundView ).
    let greenViewMarginLeft = 100
    // greenViewMarginTop, greenViewWidth, greenViewHeight are all need to be calculated at run time, so set it's value to 0 when declare.
    var greenViewMarginTop = 0
    var greenViewWidth = 0
    var greenViewHeight = 0
    

    override func viewDidLoad() {
        super.viewDidLoad()
        
        drawViews()
        
    }
    
    // This function will be invoked when iOS device change it's screen orientation between portrait and landscape.
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        
        // Because screen orientation is changed, so exchange screen width and height.
        let tmpScreenWidth = screenWidth
        screenWidth = screenHeight
        screenHeight = tmpScreenWidth
        
        // When screen orientation is changed, remove backgroundView from screen, then create and add it again in later source code.
        backgroundView.removeFromSuperview()
        
        drawViews()
    }
    
    
    func drawViews() -> Void{
        
        // Calculate greenViewMarginTop value.
        greenViewMarginTop = Int(redViewHeight) + 20
        
        // Calculate greenViewWidth value. To make green view fully visible in app screen so minus green view margin left and double background view margin.
        greenViewWidth = Int(screenWidth) - 2*Int(backgroundViewMargin) - greenViewMarginLeft - Int(redViewMargin)
        
        greenViewHeight = Int(screenHeight) - 2*Int(backgroundViewMargin) - greenViewMarginTop - Int(redViewMargin)
        
        // Add backgroundView to screen and create constraint use NSLayoutConstraint.
        addBackgroundViewByNSLayoutConstraint()
        
        // Add redView to backgroundView and create constraint use NSLayoutAnchor
        addViewConstraintsByNSLayoutAnchor()
        
        // Add greenView to backgroundView and create constraint use visual format language.
        addViewConstraintsByVFL()
        
    }
    

    /* Create background view and add constraints to it use NSLayoutConstraint class. */
    func addBackgroundViewByNSLayoutConstraint() -> Void{
        
        backgroundView = UIView()
        
        // Set background view background color.
        backgroundView.backgroundColor = UIColor.lightGray
        
        // Do not need to set backgroundView frame. 
        //backgroundView.frame = CGRect(x:self.backgroundViewPositionX, y:self.backgroundViewPositionY, width:Int(self.screenWidth) - 2 * self.backgroundViewPositionX, height:Int(self.screenHeight) - 2 * self.backgroundViewPositionY)
        
        /* Must set the UIView's property translatesAutoresizingMaskIntoConstraints's value to false before add constraints to the UIView object. Otherwise you will get error message like [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.*/
        backgroundView.translatesAutoresizingMaskIntoConstraints = false
        
        // Add backgroundView to the screen main view.
        self.view.addSubview(backgroundView)
        
        // Below code use NSLayoutConstraint class's init method to add constraints to backgroundView.
        
        // Set top constraint to backgroundView.
        let topConstraint = NSLayoutConstraint.init(item: backgroundView, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: backgroundViewMargin)
        
        // Activate top constraint to make it take effect.
        topConstraint.isActive = true
        
        // Set bottom constraint to backgroundView.
        let bottomConstraint = NSLayoutConstraint.init(item: backgroundView, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1.0, constant: -backgroundViewMargin)
        
        // Activate bottom constraint to enable it.
        bottomConstraint.isActive = true
        
        // Set and activate left, right constraint to backgroundView.
        let leftConstraint = NSLayoutConstraint.init(item: backgroundView, attribute: NSLayoutConstraint.Attribute.left, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.left, multiplier: 1.0, constant: backgroundViewMargin)
        
        leftConstraint.isActive = true
        
        let rightConstraint = NSLayoutConstraint.init(item: backgroundView, attribute: NSLayoutConstraint.Attribute.right, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.right, multiplier: 1.0, constant: -backgroundViewMargin)
        
        rightConstraint.isActive = true
    }

    
    /* Create red view and add it as background view's subview, use NSLayoutAnchor class to add constraints to it, so the constraint is relative to backgroundView. */
    func addViewConstraintsByNSLayoutAnchor() -> Void{
        
        // Set red view's background color.
        redView.backgroundColor = UIColor.red
        
        // Set red view's translatesAutoresizingMaskIntoConstraints property to false because we want to add constraints to red view to programmatically change it's size and position responsively.
        redView.translatesAutoresizingMaskIntoConstraints = false
        
        // Add redView as subview of background view.
        backgroundView.addSubview(redView)
        
        // Below code use NSLayoutAnchor to add constraints to above red view.
        
        // Add and activate top constraint for red view.
        let topConstraint = redView.topAnchor.constraint(equalTo: backgroundView.topAnchor, constant: redViewMargin)
        topConstraint.isActive = true
        
        // Add and activate left constraint for red view.
        let leftConstraint = redView.leftAnchor.constraint(equalTo: backgroundView.leftAnchor, constant: redViewMargin)
        leftConstraint.isActive = true
        
        // Add and activate red view width constraint.
        let widthConstraint = redView.widthAnchor.constraint(equalToConstant: redViewWidth)
        widthConstraint.isActive = true
        
        // Add and activate red view height constraint.
        let heightConstraint = redView.heightAnchor.constraint(equalToConstant: redViewHeight)
        heightConstraint.isActive = true
        
    }
    
    /* Create a green view and add it as backgroundView's subview. Use visual format language to add constraints to the green view, it is relative to backgroundView. */
    func addViewConstraintsByVFL() -> Void{
    
        // Set green view background color.
        greenView.backgroundColor = UIColor.green
        
        // Set it's translatesAutoresizingMaskIntoConstraints property to false to enable add constraints to it in programmatically.
        greenView.translatesAutoresizingMaskIntoConstraints = false
        
        // Add greenView as backgroundView's child.
        backgroundView.addSubview(greenView)
        
        // Add constraints use visual format language text.
        
        // The horizontal visual format language string. This string text means the greenView's width is the value of greenViewWidth variable, and the space direction is between greenView left edge to the parent view left edge. The left and greenView is a placeholder string.
        let horizontalVFL = "H:|-left-[greenView(\(greenViewWidth))]"
        
        // The vertical visual format language string. This string text means the greenView's height is the value of variable greenViewHeight, and the space direction is between greenView top edge to the parent view top edge. The string top and greenView is placeholder string also.
        let verticalVFL = "V:|-top-[greenView(\(greenViewHeight))]"
        
        // The metrics is a dictionary object which specify the left and top placeholder's value, the key must be same as placeholder string in above visual format language string.
        let metrics = ["left":greenViewMarginLeft, "top":greenViewMarginTop]
        
        // The views is a dictionary object which specify the view object in the visual format language string, the key should be same with the view's placeholder name in the VFL string.
        let views = ["greenView": greenView]
        
        // Create horizontal constraint to greenView.
        let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: horizontalVFL, options: NSLayoutConstraint.FormatOptions.alignAllLeading, metrics: metrics, views: views)
        
        // Create vertical constraint to greenView.
        let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: verticalVFL, options: NSLayoutConstraint.FormatOptions.alignAllLeading, metrics: metrics, views: views)
        
        // Add above horizontal and vertical constraints to backgroundView.
        backgroundView.addConstraints(horizontalConstraints)
        backgroundView.addConstraints(verticalConstraints)
    }
}

Reference

  1. How To Create A Swift Project In Xcode

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.