Hello,
I try to do the same as the UIListContentConfiguration
. Because I want to have a UITableViewCell with an image at beginning, then 2 Labels and an image at the end (accessoryView/Type should also be usable). I also want to have the dynamic behavior of the two labels, that if one or both of them exceeds a limit, that they are put under each under. But I cannot use the UIListContentConfiguration.valueCell, because of the extra image at the end.
So I have tried to make a TextWrapper
as an UIView
, which contains only the two UILabels and the TextWrapper
should take care of the dynamic height of the UILabels and put them side to side or under each other.
But here in this post Im only concentrating on the issue with the labels under each other, because I have managed it to get it working, that I have two sets of Constraints and switch the activeStatus of the constraints, depending on the size of the two labels. But currently only the thing with the labels under each under makes problems.
Following approaches I have tried for the TextWrapper
:
- Using only constraints in this Wrapper (results in ambiguous constraints)
- Used combinations of constraints + intrinsicContentSize (failed because it seems that invalidateIntrinsicContentSize doesn't work for me)
Approach with constraints only
Following code snippet results in ambiguous vertical position and height. Because I have 3 constraints (those with the defaultHigh priorities).
class TextWrapper: UIView {
let textLabel = UILabel()
let detailLabel = UILabel()
init() {
super.init(frame: .zero)
self.addSubview(self.textLabel)
self.addSubview(self.detailLabel)
self.textLabel.numberOfLines = 0
self.detailLabel.numberOfLines = 0
self.directionalLayoutMargins = .init(top: 8, leading: 16, bottom: 8, trailing: 8)
self.translatesAutoresizingMaskIntoConstraints = false
self.textLabel.translatesAutoresizingMaskIntoConstraints = false
self.detailLabel.translatesAutoresizingMaskIntoConstraints = false
// Content Size
self.textLabel.setContentCompressionResistancePriority(.required, for: .vertical)
self.detailLabel.setContentCompressionResistancePriority(.required, for: .vertical)
// Constraints
self.textLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true
self.textLabel.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor).constraint(with: .defaultHigh)
self.textLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true
self.detailLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true
self.detailLabel.topAnchor.constraint(equalTo: self.textLabel.bottomAnchor, constant: 2).constraint(with: .defaultHigh)
self.detailLabel.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor).constraint(with: .defaultHigh)
self.detailLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Approach with intrinsicContentSize
Pretty similar to the above, with only difference that I invalidate the intrinsicContentSize in the layoutSubviews, because at the first call of the intrinsicContentSize the width of the View is zero. I also tried different things like setNeedsLayout
with layoutIfNeeded
but nothing really works. After I invalidate the intrinsicContentSize in the layoutSubviews the intrinsicContentSize is called with the correct width of the View and calculates the correct height, but the TableView doesn't update the height accordingly.
class TextWrapper: UIView {
let textLabel = UILabel()
let detailLabel = UILabel()
init() {
super.init(frame: .zero)
self.addSubview(self.textLabel)
self.addSubview(self.detailLabel)
self.textLabel.numberOfLines = 0
self.detailLabel.numberOfLines = 0
self.directionalLayoutMargins = .init(top: 8, leading: 16, bottom: 8, trailing: 8)
self.translatesAutoresizingMaskIntoConstraints = false
self.textLabel.translatesAutoresizingMaskIntoConstraints = false
self.detailLabel.translatesAutoresizingMaskIntoConstraints = false
// Content Size
self.textLabel.setContentCompressionResistancePriority(.required, for: .vertical)
self.detailLabel.setContentCompressionResistancePriority(.required, for: .vertical)
// Constraints
self.textLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true
self.textLabel.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor).constraint(with: .defaultHigh)
self.textLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true
self.detailLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor).isActive = true
self.detailLabel.topAnchor.constraint(equalTo: self.textLabel.bottomAnchor, constant: 2).constraint(with: .defaultHigh)
self.detailLabel.widthAnchor.constraint(lessThanOrEqualTo: self.layoutMarginsGuide.widthAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
let maxLabelWidth = self.bounds.width
guard maxLabelWidth > 0 else {
// The first time it has a width of 0, so we are giving a default height in this case, to dont produce a error in TableView
return CGSize(width: UIView.noIntrinsicMetric, height: 44)
}
let textLabelSize = self.textLabel.sizeThatFits(CGSize(width: maxLabelWidth, height: .greatestFiniteMagnitude))
let detailLabelSize = self.detailLabel.sizeThatFits(CGSize(width: maxLabelWidth, height: .greatestFiniteMagnitude))
let totalHeight = textLabelSize.height + detailLabelSize.height + 16
return CGSize(width: UIView.noIntrinsicMetric, height: totalHeight)
}
override func layoutSubviews() {
super.layoutSubviews()
self.invalidateIntrinsicContentSize()
}
I also tried to use the intrinsicContentSize with only layoutSubviews, but this haven't worked also.
Does anybody have ran into such issues? And know how to fix that?