When adjusting a label's text for self seizing collectionView cells I use the following function in sizeForItem:
func estimatedLabelHeight(text: String, width: CGFloat, font: UIFont) -> CGFloat {
let size = CGSize(width: width, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let attributes = [NSAttributedStringKey.font: font]
let rectangleHeight = String(text).boundingRect(with: size, options: options, attributes: attributes, context: nil).height
return rectangleHeight
}
The function works fine and my cells expand accordingly.
What has happened though is there are several line breaks inside some of the text from the pricesLabel that get fed into the function. The line breaks are too "tight" so I followed this answer when creating my label to expand the spacing in between the line breaks.
let attributedString = NSMutableAttributedString(string: pricesText)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 7 // if I set this at 2 I have no problems
attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedString.length))
The problem is the text from pricesLabel inside the cell expands to far downwards. Using paragraphStyle.lineSpacing = 7
creates the space I want but it causes problems. If I set this to paragraphStyle.lineSpacing = 2
have no problems but the spacing is to tight.
As you can see in the picture the cell sizes the way it's supposed to but the the line break spacing in between the $8.00 and $12.00 makes the text expand to far and the text of $20.00 from the computedTotalLabel gets obscured.
I called sizeToFit()
in layoutSubViews()
but it made no difference:
override func layoutSubviews() {
super.layoutSubviews()
pricesLabel.sizeToFit()
computedTotalLabel.sizeToFit()
}
How can I make the pricesLabel text adjusted with the line breaks size itself accordingly
class MyCell: UICollectionViewCell {
let pricesLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .right
label.sizeToFit()
label.font = UIFont.systemFont(ofSize: 15.5)
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.numberOfLines = 0
label.sizeToFit()
return label
}()
let computedTotalLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .right
label.textColor = .black
label.sizeToFit()
label.font = UIFont.boldSystemFont(ofSize: 15.5)
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.numberOfLines = 1
label.sizeToFit()
return label
}()
let staticTotalLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Total"
label.textAlignment = .left
label.textColor = .black
label.font = UIFont.boldSystemFont(ofSize: 15.5)
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
label.numberOfLines = 1
label.sizeToFit()
return label
}()
let separatorLine: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .lightGray
return view
}()
override func layoutSubviews() {
super.layoutSubviews()
pricesLabel.sizeToFit()
computedTotalLabel.sizeToFit()
}
var myObject: MyObject? {
didSet {
// text is "$8.00\n$12.00\n"
let pricesText = myObject?.myText ?? "error"
let attributedString = NSMutableAttributedString(string: pricesText, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 15.5)])
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 7
attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, attributedString.length))
pricesLabel.attributedText = attributedString
computedTotalLabel.text = functionThatTalliesUpAllThePrices(pricesText)
configureAnchors()
}
}
func configureAnchors() {
addSubview(pricesLabel)
addSubview(totalLabel)
addSubview(staticTotalLabel) // this is the label on the left side of the pic that says Total:
addSubview(separatorLine)
pricesLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 12).isActive = true
pricesLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10).isActive = true
staticTotalLabel.lastBaselineAnchor.constraint(equalTo: totalLabel.lastBaselineAnchor).isActive = true
staticTotalLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10).isActive = true
staticTotalLabel.rightAnchor.constraint(equalTo: totalLabel.leftAnchor, constant: -10).isActive = true
computedTotalLabel.topAnchor.constraint(equalTo: pricesLabel.bottomAnchor, constant: 0).isActive = true
computedTotalLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10).isActive = true
separatorLine.topAnchor.constraint(equalTo: computedTotalLabel.bottomAnchor, constant: 12).isActive = true
separatorLine.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10).isActive = true
separatorLine.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10).isActive = true
separatorLine.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
separatorLine.heightAnchor.constraint(equalToConstant: 1).isActive = true
}
}
This is the sizeForItem inside the collectionView cell. Not sure if this makes a difference to the problem so I added it anyway
class MyClass: UIViewController {
let tableData = [MyObect]()
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let myObect = tableData[indexPath.item]
// text is "$8.00\n$12.00\n"
let pricesText = myObject?.myText ?? "error"
let width = collectionView.frame.width
let pricesLabelHeight = estimatedLabelHeight(text: pricesText, width: width, font: UIFont.systemFont(ofSize: 15.5))
let total = functionThatTalliesUpAllThePrices(pricesText)
let totalLabelHeight = estimatedLabelHeight(text: functionThatAddsUp, width: width, font: UIFont.boldSystemFont(ofSize: 15.5))
// the 12 + 0 + 12 + 1 are the constant sizes I use inside the cell's configureAnchors functions
let cellHeight = 12 + pricesLabelHeight + 0 + totalLabelHeight + 12 + 1
return CGSize(width: width, height: ceil(cellHeight))
}
}