iosswiftautolayoutconstraintsvisual-format-language

How to center two views in super view with greater than or equal to constraints


I made an example ViewController with two Labels to highlight my issue. The goal is to vertically separate the labels by 10, and then center them vertically using greater than or equal to constraints. I'm using visual format, but this should apply if I setup my constraints like view.topAnchor.constraint(greaterThan.... I also have two constraints to horizontally layout the labels

My ViewController:

class myVC: UIViewController {
    lazy var titleLabel: UILabel = {
        let l = UILabel(frame: .zero)
        l.translatesAutoresizingMaskIntoConstraints = false
        l.text = "Hello World"
        l.font = .systemFont(ofSize: 50)
        l.textColor = .black
        return l
    }()

    lazy var descLabel: UILabel = {
        let l = UILabel(frame: .zero)
        l.translatesAutoresizingMaskIntoConstraints = false
        l.text = "description"
        l.font = .systemFont(ofSize: 35)
        l.textColor = .gray
        return l
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .yellow
        view.addSubview(titleLabel)
        view.addSubview(descLabel)
        titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        descLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor).isActive = true
        NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(<=50)-[titleLabel]-(10)-[descLabel]-(<=50)-|", options: .init(), metrics: nil, views: ["titleLabel": titleLabel, "descLabel": descLabel]))
    }

}

This results in wrong constraints. From my understanding, this SHOULD separate the views by 10 pts, and center the labels vertically because in the format "V:|-(<=50)-[titleLabel]-(10)-[descLabel]-(<=50)-|" I say that the distance between the Title Label's top and the superView's top should be at least (greaterThanOrEqualTo) 50, and the distance between the description Label's bottom and the superView's bottom should be at least 50. What should my top and bottom constraints look like if I want to center the two labels vertically?

Yes, I realize I can just set vertical and horizontal centers, but this is an example I made for a problem I can't use those for. I need to be able to center the View with greater(or less) than or equal to constraints.


Solution

  • It's very difficult to center elements using VFL.

    It's also difficult to center two elements unless they are embedded in a UIView or a UIStackView.

    Here is one option by embedding the labels in a "container" UIView:

    class MyVC: UIViewController {
        lazy var titleLabel: UILabel = {
            let l = UILabel(frame: .zero)
            l.translatesAutoresizingMaskIntoConstraints = false
            l.text = "Hello World"
            l.font = .systemFont(ofSize: 50)
            l.textColor = .black
    
            // center the text in the label - change to .left if desired
            l.textAlignment = .center
    
            return l
        }()
    
        lazy var descLabel: UILabel = {
            let l = UILabel(frame: .zero)
            l.translatesAutoresizingMaskIntoConstraints = false
            l.text = "description"
            l.font = .systemFont(ofSize: 35)
            l.textColor = .gray
    
            // center the text in the label - change to .left if desired
            l.textAlignment = .center
    
            return l
        }()
    
        lazy var containerView: UIView = {
            let v = UIView()
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            view.backgroundColor = .yellow
    
            // give the labels and containerView background colors to make it easy to see the layout
            titleLabel.backgroundColor = .green
            descLabel.backgroundColor = .cyan
            containerView.backgroundColor = .blue
    
            // add containerView to view
            view.addSubview(containerView)
    
            // add labels to containerView
            containerView.addSubview(titleLabel)
            containerView.addSubview(descLabel)
    
            NSLayoutConstraint.activate([
    
                // constrain titleLabel Top to containerView Top
                titleLabel.topAnchor.constraint(equalTo: containerView.topAnchor),
    
                // constrain titleLabel Leading and Trailing to containerView Leading and Trailing
                titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
                titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
    
                // constrain descLabel Leading and Trailing to containerView Leading and Trailing
                descLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
                descLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
    
                // constrain descLabel Bottom to containerView Bottom
                descLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
    
                // constrain descLabel Top 10-pts from titleLabel Bottom
                descLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10.0),
    
                // constrain containerView centered horizontally and vertically
                containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
    
            ])
    
        }
    
    }
    

    Result:

    enter image description here