I am currently developing an iOS application and facing a challenge with UILabel. I wish to achieve two main objectives:
- Limit the display of text within a UILabel to only two lines.
- Append "...More" at the end of the text to indicate that there is additional content available.
Now I made a calculating logic below: but it take too much time to calculating so screen is slightly flicker.
import UIKit
extension UILabel {
func getMaxHeight(font: UIFont, width: CGFloat) -> CGFloat {
let maxSize = CGSize(width: width, height: CGFloat(MAXFLOAT))
let text = (self.text ?? "") as NSString
let textHeight = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil).height
return textHeight
}
func getNumberOfLines(font: UIFont, width: CGFloat) -> Int {
let maxSize = CGSize(width: width, height: CGFloat(MAXFLOAT))
let text = (self.text ?? "") as NSString
let textHeight = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil).height
let lineHeight = font.lineHeight
return Int(ceil(textHeight / lineHeight))
}
private func getNumberOfLines(text: String, font: UIFont, width: CGFloat) -> Int {
let maxSize = CGSize(width: width, height: CGFloat(MAXFLOAT))
let text = text as NSString
let textHeight = text.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil).height
let lineHeight = font.lineHeight
return Int(ceil(textHeight / lineHeight))
}
func replaceEllipsis(with string: String, font: UIFont, width: CGFloat, maxLine: Int, highlight: String? = nil) {
guard let text = self.text else { return }
lineBreakMode = .byClipping
if self.getNumberOfLines(font: font, width: width) <= maxLine {
return
}
let totalNumberOfLine = getNumberOfLines(text: text, font: font, width: width)
let charArray: [Character] = text.map { $0 }
let chunkSize = charArray.count / totalNumberOfLine
var resultArray: [String] = []
for i in 0..<totalNumberOfLine {
let startIndex = i * chunkSize
let endIndex = min((i + 1) * chunkSize, charArray.count)
let chunk = charArray[startIndex..<endIndex]
let chunkString = chunk.reduce("") { partialResult, char in
return partialResult + "\(char)"
}
resultArray.append(chunkString)
}
let textForTwo = resultArray[0..<maxLine+1].joined()
var estimatedText = textForTwo
var estimatedLine: Int = 10
repeat {
estimatedText.removeLast()
self.text = estimatedText
self.text?.append(string)
estimatedLine = self.getNumberOfLines(font: font, width: width)
} while estimatedLine > maxLine
estimatedText.append(string)
let attributedString = NSMutableAttributedString(string: estimatedText)
if let highlight = highlight {
let length = highlight.count
let lastIndex = estimatedText.index(estimatedText.endIndex, offsetBy: -length)
let startPoint = estimatedText.distance(from: estimatedText.startIndex, to: lastIndex)
let range = NSRange(location: startPoint, length: length)
attributedString.addAttribute(.foregroundColor, value: UIColor.gray, range: range)
self.text = ""
self.attributedText = attributedString
}
}
}
Please refer this for read more and read less.
Output