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 单元格设置锚点以调整换行符的间距的主要内容,如果未能解决你的问题,请参考以下文章

iOS中的自定大小集合视图单元格

iOS UICollectionView 与自定尺寸项目错误?

IOS - 自定尺寸单元问题

如何使用 FCM 在 ios 推送通知中播放自定义通知声音

ios10自定手势有啥用

如何在键盘视图和顶视图中添加自定义子视图? (iOS8 及以下无法在 iOS9 中使用)