How to calculate the size sample solution of text using Swift

  • 2020-05-30 21:10:16
  • OfStack

preface

For the grammar of swift, which is still in the groping stage, many people are not familiar with it. This paper mainly introduces the relevant content of computing size by Swift, which can be Shared for your reference and learning.

iOS 11 was used to limit the width and height of the calculated string size UILabel的textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect Method, thread safety was not considered at the time (low exploded), Xcode did not prompt, used several versions, fortunately 1 straight no problem.

Post the method (I won't explain why I chose this method at that time) :


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
  }

I recently upgraded Xcode 9 to warn me at run time let calculatedLabel = UILabel() Only when the main thread was executed did I realize the seriousness of the problem and immediately modified it:


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 {
      //  The height of the text minus the height of the font is less than or equal to the line spacing 1 line 
      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
  }
}

Parameter interpretation

constrainedSize: restricted size font: size lineSpacing: the default is nil, using the system default line spacing lines: number of restricted rows

Note: the code version is Swift 4.0

The two methods above substitute names: method 1 and method 2, respectively.

Method 1: limit the width and height, set the line spacing, and calculate accurately

Method 2: more row restriction than method 1.

Extended methods in conjunction with UILabel:


extension UILabel {
  
  //  Set up the `numberOfLines = 0` The reason: 
  //  With method `func boundingRect(with constrainedSize: CGSize, font: UIFont, lineSpacing: CGFloat? = nil, lines: Int) -> CGSize` Use, can be a good solution to the normal display can not limit the number of lines; 
  //  If it is label Set a limit on the number of rows (greater than 0 ), using the above calculation (with line spacing), and the actual number of lines in the string is greater than the limit number, the height will be label Cannot display properly. 
  func setText(with normalString: String, lineSpacing: CGFloat?, frame: CGRect) {
    self.frame = frame
    self.numberOfLines = 0
        
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineBreakMode = .byTruncatingTail
    if lineSpacing != nil {
      if (frame.height - font.lineHeight) <= lineSpacing! {
        paragraphStyle.lineSpacing = 0
      } else {
        paragraphStyle.lineSpacing = lineSpacing!
      }
    }
    let attributedString = NSMutableAttributedString(string: normalString)
    let range = NSRange(location: 0, length: attributedString.length)
    attributedString.addAttributes([NSAttributedStringKey.font: font], range: range)
    attributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraphStyle, range: range)

    self.attributedText = attributedString
  }
}

Thank you in this hamster: iOS spaced visitors and https: / / github com/zhengwenming/WeChat

conclusion


Related articles: