user599807
user599807

Reputation:

Swift autolayout multiple views/labels

I want to programmatically divide my screen into three parts. Each part shall be a label with a backgroundColor and the width is set to be:

// Get the device width
self.view.bounds.width  

The height must be dynamically. An example is in the image below. Each color is one part. The first part (the red part) must be attached to the top and the last part (the orange) must be attached to the bottom. And dependent on the label size inside each part the height shall be adjusted.Image

The blue part is always 100% and dependent of the red and orange part it subtracts from the blue part.

Anyone have any ideas of how to achieve this? Appreciate help!

Upvotes: 1

Views: 1554

Answers (1)

ABakerSmith
ABakerSmith

Reputation: 22939

To do this is best to use AutoLayout, using NSLayoutConstraints. Here's some sample code (at the moment this is all in viewDidLoad, which probably isn't the best thing to do, but I'll leave it up to you to organise the code.)

override func viewDidLoad() {
    super.viewDidLoad()

    //  1.
    var topView: UIView = self.view

    // Have as many labels as you want!
    for i in 0..<numberOfLabels {
        let label = UILabel()

        //  2.
        label.numberOfLines = 0
        label.setTranslatesAutoresizingMaskIntoConstraints(false)
        self.view.addSubview(label)

        //  3.
        if i == 0 || i == numberOfLabels - 1 {
            label.setContentHuggingPriority(UILayoutPriority.abs(1000), forAxis: UILayoutConstraintAxis.Vertical)
        }

        let widthConstraint = NSLayoutConstraint(item: label, attribute: .Width, relatedBy: .Equal, toItem: self.view, attribute: .Width, multiplier: 1.0, constant: 0.0)
        //  4.
        let heightConstraint = NSLayoutConstraint(item: label, attribute: .Height, relatedBy: .GreaterThanOrEqual, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 20.0)

        //  5.
        let attribute: NSLayoutAttribute = i == 0 ? .Top : .Bottom
        let topConstraint = NSLayoutConstraint(item: label, attribute: .Top, relatedBy: .Equal, toItem: topView, attribute: attribute, multiplier: 1, constant: 0)

        //  6.
        if i == numberOfLabels - 1 {
            let bottomConstraint = NSLayoutConstraint(item: label, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0)

            self.view.addConstraint(bottomConstraint)
        }

        label.addConstraint(heightConstraint)

        self.view.addConstraint(widthConstraint)
        self.view.addConstraint(topConstraint)

        topView = label
    }
}

1. topView is the view that the label's NSLayoutAttribute.Top will be related to. For example: the first label's top is related to its container view; the second label's top is related to the first label; the third label's top is related to the second view, and so on.

2. Set the number of lines to zero to allow for as many lines as required by label.text.

3. Set the content hugging priority of the top and bottom labels to 1000. This will cause the top and bottom labels to 'hug' the text because middle label(s), by default, have a hugging priority of 250.

4. Create an NSLayoutConstraint that sets the label to be higher than 20 points because for preferredMaxLayoutWidth to be set automatically the width and height of the label must be explicitly defined by NSLayoutConstraints.

5. This selects the attribute on topView that will pin the label to the topView. For example: if creating the first label, its top is pinned to the top of the container view. Otherwise, the top of the label is pinned to the bottom of the label above it.

6. If it's the last label, pin its bottom edge to the bottom of the container view.

Here's the result:

enter image description here

Hope that answers your question.

Upvotes: 1

Related Questions