利用Swift如何计算文本的size示例详解

2020-01-08 23:51:19王旭

前言

对于swift 还处于摸索阶段很多语法还不熟悉,本文主要给大家介绍的是关于利用Swift计算文本size的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

iOS 11之前限制宽高计算字符串的size用的是UILabel的textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect方法,当时也没考虑线程安全问题(low爆了),Xcode也没提示,用了好几个版本,所幸一直都没问题。

贴下方法(当时为什么选这个方法就不解释了):


func textSize(font: UIFont, constrainedSize: CGSize, lineSpacing: CGFloat?, lines: Int) -> CGSize {
    if self.isEmpty || lines < 0 {
      return CGSize.zero
    }
    
    let attributedString = NSMutableAttributedString(string: self)
    let range = NSRange(location: 0, length: attributedString.length)
    attributedString.addAttributes([NSFontAttributeName: font], range: range)
    if lineSpacing != nil {
      let paragraphStyle = NSMutableParagraphStyle()
      paragraphStyle.lineBreakMode = .byTruncatingTail
      paragraphStyle.lineSpacing = lineSpacing!
      attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: range)
    }
    
    let calculatedLabel = UILabel()
    calculatedLabel.font = font
    calculatedLabel.attributedText = attributedString
    calculatedLabel.numberOfLines = lines
    let rect = calculatedLabel.textRect(forBounds: CGRect(x: 0, y: 0, width: constrainedSize.width, height: constrainedSize.height), limitedToNumberOfLines: lines)
    
    return rect.size
  }

最近升级了Xcode 9,运行时警告我let calculatedLabel = UILabel()要在主线程执行,这时才意识到问题的严重性,马上进行了修改:


extension String {

  func boundingRect(with constrainedSize: CGSize, font: UIFont, lineSpacing: CGFloat? = nil) -> CGSize {
    let attritube = NSMutableAttributedString(string: self)
    let range = NSRange(location: 0, length: attritube.length)
    attritube.addAttributes([NSAttributedStringKey.font: font], range: range)
    if lineSpacing != nil {
      let paragraphStyle = NSMutableParagraphStyle()
      paragraphStyle.lineSpacing = lineSpacing!
      attritube.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraphStyle, range: range)
    }
    
    let rect = attritube.boundingRect(with: constrainedSize, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
    var size = rect.size
    
    if let currentLineSpacing = lineSpacing {
      // 文本的高度减去字体高度小于等于行间距,判断为当前只有1行
      let spacing = size.height - font.lineHeight
      if spacing <= currentLineSpacing && spacing > 0 {
        size = CGSize(width: size.width, height: font.lineHeight)
      }
    }
    
    return size
  }
  
  func boundingRect(with constrainedSize: CGSize, font: UIFont, lineSpacing: CGFloat? = nil, lines: Int) -> CGSize {
    if lines < 0 {
      return .zero
    }
    
    let size = boundingRect(with: constrainedSize, font: font, lineSpacing: lineSpacing)
    if lines == 0 {
      return size
    }

    let currentLineSpacing = (lineSpacing == nil) ? (font.lineHeight - font.pointSize) : lineSpacing!
    let maximumHeight = font.lineHeight*CGFloat(lines) + currentLineSpacing*CGFloat(lines - 1)
    if size.height >= maximumHeight {
      return CGSize(width: size.width, height: maximumHeight)
    }
    
    return size
  }
}