How to put two labels horizontally by aligning their top edges in iOS?

2.8k views Asked by At

I want to create custom view like below.

enter image description here

As you see, it consists from title and price labels. Title can have million number of lines, but its top edge should be aligned with price label. It seems simple design, but it has hundreds of solution. I tried every of them, but my title label is not growing, by having dots at the end (numberOfLines = 0 doesn't help). Here is how I approached to create such a design:

  1. I created titleLabel with top, leading, trailing to price label, bottom constraints. Also, I created price label with top and trailing constraints only in order to align their top edges. I assigned compression resistance and hugging priority to price label, because it is more important and should not be ruined. Here is code if you want:

     addSubview(titleLabel)
     addSubview(priceLabel)
     titleLabel.snp.makeConstraints { make in
         make.leading.equalToSuperview().offset(16)
         make.trailing.lessThanOrEqualTo(priceLabel.snp.leading).offset(-8)
         make.top.equalToSuperview()
         make.bottom.equalToSuperview()
     }
     priceLabel.snp.makeConstraints { make in
         make.trailing.equalToSuperview().offset(-16)
         make.top.equalTo(titleLabel.snp.top)
     }
    

I created separate custom view, because I want to use it inside StackView(spacing 8, distribution fill, vertical). Result of this approach: title label's is not growing. It has only one line with dots at the end, if it has big text.

  1. Second approach was to create stackView (horizontally, spacing 8, distribution fill, alignment top). I set alignment top in order to align top edges of the labels. The result was the as in approach #1.

How to solve this problem? Where am I wrong? It seems I don't see something core in Auto Layout theory here.

2

There are 2 answers

0
DonMag On BEST ANSWER

During development of your layout, it can be very helpful to use contrasting colors for element backgrounds... makes it really easy to see what's happening with their frames.

Give this a try...

Custom view class

class NeoCustomView: UIView {
    
    let titleLabel = UILabel()
    let priceLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.numberOfLines = 0
        titleLabel.font = .systemFont(ofSize: 17)
        
        priceLabel.translatesAutoresizingMaskIntoConstraints = false
        priceLabel.font = .boldSystemFont(ofSize: 17)
        
        priceLabel.setContentHuggingPriority(.required, for: .horizontal)
        priceLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
        
        addSubview(titleLabel)
        addSubview(priceLabel)
        
        titleLabel.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(16)
            make.trailing.lessThanOrEqualTo(priceLabel.snp.leading).offset(-8)
            make.top.equalToSuperview()
            make.bottom.equalToSuperview()
        }
        priceLabel.snp.makeConstraints { make in
            make.trailing.equalToSuperview().offset(-16)
            make.top.equalTo(titleLabel.snp.top)
        }

        // use some background colors so we can easily see the frames
        backgroundColor = .red
        titleLabel.backgroundColor = .yellow
        priceLabel.backgroundColor = .green
        
    }
    
}

Example view controller class - adds another label constrained 4-pts from the bottom of the custom view so we can see everything working:

class NeoViewController: UIViewController {
    
    let testView = NeoCustomView()
    let anotherLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        anotherLabel.translatesAutoresizingMaskIntoConstraints = false
        anotherLabel.font = .systemFont(ofSize: 15)
        anotherLabel.backgroundColor = .blue
        anotherLabel.textColor = .white
        anotherLabel.textAlignment = .center
        anotherLabel.numberOfLines = 0
        anotherLabel.text = "This label is constrained 4 points from the bottom of the custom view."
        
        view.addSubview(testView)
        view.addSubview(anotherLabel)
        
        testView.snp.makeConstraints { make in
            make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16)
            make.top.equalTo(view.safeAreaLayoutGuide).offset(40)
        }
        anotherLabel.snp.makeConstraints { make in
            make.top.equalTo(testView.snp.bottom).offset(4)
            make.width.equalTo(testView.snp.width)
            make.centerX.equalTo(testView.snp.centerX)
        }
        
        testView.titleLabel.text = "This is long text for the title label that will word wrap when it needs to."
        testView.priceLabel.text = "300$"
    }
}

Result (Red is custom view with title and price labels, Blue is a label added and constrained below the custom view):

enter image description here

0
Teju Amirthi On

Add a new height constraint as well for the title label with
relation: greater than equal to
contant: some constant(might be 20 or something based on your fontsize and content).

I hope this solves your issue