Search code examples
iosswiftuilabelnslayoutconstraintsnapkit

How to set UIView height equal to max of two subviews UILabel?


I have to UILabel with dynamic height. I want to set it superview height equal to max of UILabel heights.

class ComponentCell: UIView {
  private lazy var leftRow: UILabel = UILabel()
  private lazy var rightRow: UILabel = UILabel()

  init(leftValue: String, rightValue: String) {
    super.init(frame: .zero)
    leftRow.backgroundColor = .red
    leftRow.numberOfLines = 0
    leftRow.lineBreakMode = .byWordWrapping
    leftRow.text = leftValue
    rightRow.text = rightValue
    rightRow.backgroundColor = .yellow
    rightRow.numberOfLines = 0
    rightRow.lineBreakMode = .byWordWrapping
    self.addSubview(self.leftRow)
    self.addSubview(self.rightRow)
    leftRow.sizeToFit()
    rightRow.sizeToFit()
    leftRow.setContentHuggingPriority(.required, for: .vertical)
    rightRow.setContentHuggingPriority(.required, for: .vertical)
    self.translatesAutoresizingMaskIntoConstraints = false
    self.leftRow.snp.makeConstraints { make in
      make.top.equalToSuperview()
      make.left.equalToSuperview()
      make.width.equalToSuperview().dividedBy(2)
    }
    self.rightRow.snp.makeConstraints { make in
      make.top.equalToSuperview()
      make.right.equalToSuperview()
      make.width.equalToSuperview().dividedBy(2)
    }
    self.layoutIfNeeded()
  }

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

If I set leftRow.botton.equalTo(superview.bottom) and rightRow.botton.equalTo(superview.bottom) it's working. But I think is not a good way. And I don't understand why setContentHuggingPriority not helped me to solve this problem.


Solution

  • Content Hugging

    Content hugging leads more likely to squeeze your labels. What you want is the height of the labels to be more respected. So you'd rather use compression resistance priority. However you actually need neither of those.

    Layout Constraints

    Since you're setting your constraints programatically, you'll need to set translatesAutoresizingMaskIntoConstraints to false for your labels as well:

    leftRow.translatesAutoresizingMaskIntoConstraints = false
    rightRow.translatesAutoresizingMaskIntoConstraints = false
    

    The bottom constraint is actually a good start, but you don't want to fit the height of the smaller label unnecessarily to the height of the bigger label. So you would want to add a constraint that is "less than or equal to the bottom anchor":

    make.bottom.lessThanOrEqualTo(self.snp.bottom)
    

    Lazy Variables

    If you want to use lazy variables you'll have to change the way there being initialized. The way you've written it, it initializes the variables right away when initializing the class. But you only want them to be initialized when they're used the first time. For that you need to write it like this:

    private lazy var leftRow: UILabel = {
        return UILabel() 
    }()
    private lazy var rightRow: UILabel = {
        return UILabel() 
    }()
    

    However in your case you don't need lazy loading, so you can initialize them directly:

    private let leftRow = UILabel()
    private let rightRow = UILabel()
    

    Other

    Since you're using layout constraints, you don't need to call sizeToFit on the labels. It doesn't do anything.

    Calling layoutIfNeeded() within the init doesn't do anything either since it will be called anyway once you add ComponentCell as a subview to another view.