iOS - 如何使用自定大小的 CollectionView 单元格设置锚点以调整换行符的间距
Posted
技术标签:
【中文标题】iOS - 如何使用自定大小的 CollectionView 单元格设置锚点以调整换行符的间距【英文标题】:iOS -How to To Set Anchors to Adjust Spacing for Line Break with Self Sizing CollectionView Cells 【发布时间】:2018-11-10 02:49:01 【问题描述】:当调整标签的文本以自动获取 collectionView 单元格时,我在 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
该功能工作正常,我的单元格相应扩展。
但发生的情况是,在价格标签的某些文本中存在几个换行符,这些文本被输入到函数中。换行符太“紧”,所以我跟着this answer when creating my label 扩大了换行符之间的间距。
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))
问题是单元格内的价格标签中的文本会向下扩展。使用paragraphStyle.lineSpacing = 7
创建了我想要的空间,但它会导致问题。如果我将此设置为paragraphStyle.lineSpacing = 2
没有问题,但间距太紧了。
正如您在图片中看到的那样,单元格的大小符合预期,但 $8.00 和 $12.00 之间的换行符间距使文本扩大到较远,并且来自 computedTotalLabel 的 $20.00 文本变得模糊。
我在layoutSubViews()
中调用了sizeToFit()
,但没有任何区别:
override func layoutSubviews()
super.layoutSubviews()
pricesLabel.sizeToFit()
computedTotalLabel.sizeToFit()
如何使价格标签文本根据换行符本身的大小进行相应调整
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
这是 collectionView 单元格内的 sizeForItem。不确定这是否会对问题产生影响,所以我还是添加了它
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))
【问题讨论】:
【参考方案1】:第一。我必须将相同的 estimatedLabelHeight(text: String, width: CGFloat, font: UIFont)
放在 collectionView 单元格本身中。
第二。在其底部的 configureAnchors 函数中,我调用 pricesLabel.sizeToFit()
和 pricesLabel.layoutSubviews()
,然后从步骤 1 调用上述函数以从文本中获取价格标签的高度。
第三。我将pricesLabel.heightAnchor.constraint(equalToConstant:)
设置为从第 2 步返回的高度。
class MyCell: UICollectionViewCell
// step 1. place this function inside the collectionView cell
func estimatedLabelHeight(text: String, width: CGFloat, font: UIFont) -> CGFloat
let size = CGSize(width: width, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union([.usesLineFragmentOrigin, .usesFontLeading])
let attributes = [NSAttributedStringKey.font: font]
let rectangleHeight = String(text).boundingRect(with: size, options: options, attributes: attributes, context: nil).height
return rectangleHeight
func configureAnchors()
// all the other anchors are here
pricesLabel.sizeToFit()
computedTotalLabel.sizeToFit()
computedTotalLabel.layoutIfNeeded()
pricesLabel.layoutIfNeeded()
let pricesLabelText = pricesLabel.text ?? "error"
let width = self.frame.width
// step 2.
let pricesLabelHeight = estimatedLabelHeight(text: pricesLabelText, width: width, font: UIFont.systemFont(ofSize: 15.5))
// step 3.
pricesLabel.heightAnchor.constraint(equalToConstant: pricesLabelHeight).isActive = true
【讨论】:
以上是关于iOS - 如何使用自定大小的 CollectionView 单元格设置锚点以调整换行符的间距的主要内容,如果未能解决你的问题,请参考以下文章