I want to create custom view like below.
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:
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.
How to solve this problem? Where am I wrong? It seems I don't see something core in Auto Layout theory here.
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):