egze egze - 4 months ago 8
iOS Question

NSLayoutConstraint for unknown number of subviews in code

I'm getting into iOS programming and mastered more or less autolayout with fixed number of items. Say, we have a UILabel for title, UILabel for subtitle, then the visual format for the constraint is

'V:|-[title]-10-[subtitle]-|'


But what if I create subviews dynamically based on some API response. There are for example 40 subviews I need to add. It's not realistic anymore that I specify each subview with the visual format and keep track of them. What is the the proper way?

I imagine that after each subview I add, I then set the constraint based on the previous view with
constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
. Is that the way to go, or there is a better way?

Answer

It's not realistic anymore that I specify each subview with the visual format and keep track of them

Why on earth not? You seem to think this is some sort of "either/or" situation. Nothing prevents you from using visual formatting to build up a collection of constraints. No law says that you have to put all your constraints into one visual format.

Consider this code where I build up the constraints for a scroll view and thirty labels inside it:

    var con = [NSLayoutConstraint]()
    con.appendContentsOf(
        NSLayoutConstraint.constraintsWithVisualFormat(
            "H:|[sv]|",
            options:[], metrics:nil,
            views:["sv":sv]))
    con.appendContentsOf(
        NSLayoutConstraint.constraintsWithVisualFormat(
            "V:|[sv]|",
            options:[], metrics:nil,
            views:["sv":sv]))
    var previousLab : UILabel? = nil
    for i in 0 ..< 30 {
        let lab = UILabel()
        // lab.backgroundColor = UIColor.redColor()
        lab.translatesAutoresizingMaskIntoConstraints = false
        lab.text = "This is label \(i+1)"
        sv.addSubview(lab)
        con.appendContentsOf(
            NSLayoutConstraint.constraintsWithVisualFormat(
                "H:|-(10)-[lab]",
                options:[], metrics:nil,
                views:["lab":lab]))
        if previousLab == nil { // first one, pin to top
            con.appendContentsOf(
                NSLayoutConstraint.constraintsWithVisualFormat(
                    "V:|-(10)-[lab]",
                    options:[], metrics:nil,
                    views:["lab":lab]))
        } else { // all others, pin to previous
            con.appendContentsOf(
                NSLayoutConstraint.constraintsWithVisualFormat(
                    "V:[prev]-(10)-[lab]",
                    options:[], metrics:nil,
                    views:["lab":lab, "prev":previousLab!]))
        }
        previousLab = lab

I'm using visual formatting, but I'm doing it one constraint as a time as I add views (which is exactly what you're asking about).

Comments