Content hugging and content compression resistance with minimum margin between views

102 Views Asked by At

I was trying to create a UI with two labels placed horizontally in parallel to other edges on the screen so that they can grow according to the content they receive.

enter image description here

and

enter image description here

I tried setting up content hugging and compression resistance priority and was able to have labels grow based on content but when either of the labels received even more content, they started overlapping each other.

enter image description here

and

enter image description here

how do set up constraints? in such a way that labels can grow based on the content they receive, if the content is bigger then they should start truncating themselves so there should always be a minimum margin in between so that they don't start overlapping each other and if content for both labels is big enough then they should take up the space divided equally.

1

There are 1 best solutions below

3
HangarRash On

The simplest solution is to use a UIStackView, no code required. Add a horizontal UIStackView to the storyboard then add the two labels to the stack view.

Set desired constraints on the leading and trailing anchors of the stack view to position it in the view controller's view. Add a constraint to just one of the top, bottom, or centerY anchor to position the stack view vertically. The height of the stack view will come from the labels.

Ensure the stack view properties are as follows:

  • Axis: horizontal (default)
  • Distribution: Fill proportionally (this is the key change)
  • Alignment: Fill (default)
  • Spacing: 10 (set to the amount of space you want between the two labels)

Update the label properties such that the left label has left alignment and the right label has right alignment. Change the Line Break to suit your needs.

That's it. The stack view will, with a distribution of "Fill proportionally", ensure the width of the two labels will be adjusted to match the requirements posted in your question.


If you don't want to use a stack view, there is a way to meet your needs but it does require a bit of code to make it work. There's no way to get the width of the two labels to automatically size proportionally based on the combined width of the labels using just constraints setup in the storyboard.

I created a simple iOS app. Start with a new iOS app project. Add two labels to the view controller's safe area. In the storyboard, add the following constraints:

  • Set the left label's leading constraint
  • Set the right label's trailing constraint
  • Set the vertical position of both labels (presumably the same)
  • Set the left label's trailing to be <= the right label's leading with a constant, of say 10, to provide a small gap between them.
  • Set the left label's width constraint to some constant (this will be adjusted in code so the value in the storyboard doesn't matter).

With those in place you will see something like the following in the storyboard:

enter image description here

In my own test I also set the following label properties:

  • Left label to left alignment
  • Left label to a line break of "Truncate tail"
  • Right label to right alignment
  • Right label to a line break of "Truncate head"

Note that all other label properties are left as defaults, including the content compression and hugging priorities.

Update ViewController.swift with the following code:

import UIKit

class ViewController: UIViewController {
    @IBOutlet var left: UILabel!
    @IBOutlet var right: UILabel!
    @IBOutlet var leftWidthC: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Test with two short strings
        //left.text = "Hello there"
        //right.text = "Goodbye"

        // Test with one long and one short string
        //left.text = "This is a really long string to see how it works"
        //right.text = "A short one"

        // Test with one short and one long string
        //left.text = "A short one"
        //right.text = "This is a really long string to see how it works"

        // Test with two long strings
        left.text = "Hello there XXX XXX XXX XXX XXX"
        right.text = "YYY YYY YYY YYY Goodbye Bob"
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // Get the width between the two ends of the two labels
        let vwidth = right.frame.maxX - left.frame.minX
        // Get the full width of the left label
        left.sizeToFit()
        let lwidth = left.frame.width
        // Get the full width of the right label
        right.sizeToFit()
        let rwidth = right.frame.width
        // Calculate the percentage of the left label width versus the combined label width
        let lp = lwidth / (lwidth + rwidth)
        // Calculate the final displayed width of the left label
        let plw = lp * vwidth
        // Update the left label's width constraint
        leftWidthC.constant = min(lwidth, plw)
        // The right label's width will automatically adjust due to other constraints
    }
}

Now connect the two labels to their outlets. Lastly, connect the left label's width constraint to its outlet.

You can now run and test the code with different label values to check the results.

enter image description here