Search code examples
iosswiftvisual-format-language

How to use Visual Format Language to set constraints in Swift?


I am having a View Controller which has a Nav Bar, I am trying to implement a Menu Bar right below the Nav Bar for which I have a separate class file(UIView). After adding the Menu Bar, I want to add an UIImageView exactly at the vertical centre of the Menu Bar View and not the Super View and have height as same as the Menu Bar View. I am using Visual Format Language. I am not really sure how to mention the view's name and how to constraint the image's height and place it in the centre of a named View. Can someone help me ?

Below are code snippets...

//MenuBar.swift

class MenuBar: UIView {


override init(frame: CGRect) {

    super.init(frame: frame)
    print("created") 

    let imageLogo = UIImage(named: "Logo")
    let imageView = UIImageView(image: imageLogo)
    self.addSubview(imageView)
    self.addConstraintWithFormat("H:|[v0]|", views: imageView)
    self.addConstraintWithFormat("V:|[v0(100)]|", views: imageView)
    backgroundColor = UIColor.red
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

//UIView Extension for the func addConstraintWithFormat(- format:, views:)

extension UIView{
func addConstraintWithFormat(_ format : String, views : UIView...) {

    var viewsDictionary = [String : UIView]()

    for(index, view) in views.enumerated(){
        let key = "v\(index)"
        view.translatesAutoresizingMaskIntoConstraints = false
        viewsDictionary[key] = view
    }
    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: viewsDictionary))

}

Solution

  • Visual Format Language allows you to apply programmatic constraints using visual syntax strings. As per the Apples Documentation, the idea is the text visually matches the layout.

    Let's break down the syntax part for better understanding:

    H: (Horizontal) //horizontal direction
    V: (Vertical) //vertical direction
    | (pipe) //superview
    - (dash) //standard spacing (generally 8 points)
    [] (brackets) //name of the object (uilabel, unbutton, uiview, etc.)
    () (parentheses) //size of the object
    == equal widths //can be omitted
    -16- non standard spacing (16 points)
    <= less than or equal to
    >= greater than or equal to
    @250 priority of the constraint //can have any value between 0 and 1000
    

    Now, in order to apply constraint to a view using visual format language, fir we need to make translatesAutoresizingMaskIntoConstraints false for the view, on which we are going to apply constraints:

    imageView.translatesAutoresizingMaskIntoConstraints = false
    

    then we need to prepare a dictionary for all the views, which are to be used in VFL like:

    let viewDictionary = NSDictionaryOfVariableBindings(imageView)
    

    then make horizontal and vertical constraints using Visual Format String as per the rules explained above:

    let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-[imageView]-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: viewsDictionary)
    let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-[imageView(100)]-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: viewsDictionary)
    

    Now, add these constants to your superview like:

    view.addConstraints(horizontalConstraints)
    view.addConstarints(verticalConstraints)
    

    PS: If you want to make view's width/height dynamic, you need to create a matrics dictionary, pass it in metrics: instead of setting it nil and then using the appropriate keyname for the value. For example:

    let metricDict = ["viewHeight":300]
    let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-[imageView(viewHeight)]-|", options: NSLayoutConstraint.FormatOptions(), metrics: metricDict, views: viewsDictionary)