在开发中经常会碰到只显示少量介绍文字,点击更多的时候才会查看更多全部内容,如何在不使用第三方库的情况下准确地在行末加上“更多”的文字呢?
我们可以为 UILabel
添加扩展方法来实现。
1. 获取每一行的文字数组
首先,获取每一行的文本,目的有两个,第一是判断文本行数是否超过了指定的行数,第二是为了获取最后一行文本以拼接“更多”的字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| private func getLines(fitWidth: CGFloat) -> [String] { guard let text = text, !text.isEmpty else { return [] } let attrString = NSMutableAttributedString(string: text) attrString.addAttributes( attributes, range: NSMakeRange(0, attrString.length)) let frameSetter = CTFramesetterCreateWithAttributedString(attrString) let textRect = CGRect(x: 0, y: 0, width: fitWidth, height: 10000) let textRectPath = UIBezierPath(rect: textRect).cgPath let frame = CTFramesetterCreateFrame( frameSetter, CFRangeMake(0, 0), textRectPath, nil) let lines = CTFrameGetLines(frame) as! [CTLine] var linesArray = [String]() for line in lines { let lineRange = CTLineGetStringRange(line) let range = NSMakeRange( lineRange.location, lineRange.length) let lineString = NSString(string: text).substring(with: range) linesArray.append(lineString) } return linesArray }
|
2. 对文本的最后一行进行拼接
首先把省略提示文字拼接到末尾,计算长度,如果长度超过可显示的宽度,则截取最后一个字。如此循环,直到文字宽度小于指定宽度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private func trimmingTail(for string: String, appendString: String, fitWidth: CGFloat) -> String { var trimmedString = string var stringWidth = calculateLength(trimmedString + appendString) while stringWidth > fitWidth { trimmedString.removeLast() stringWidth = calculateLength(trimmedString + appendString) } return trimmedString }
private func calculateLength(_ string: String) -> CGFloat { let targetString = NSString(string: string) let size = targetString.boundingRect( with: CGSize(width: 1000, height: 250), options: [.usesLineFragmentOrigin], attributes: attributes, context: nil) return size.width }
|
3. 根据规则设置
最后则是判断是否应该添加更多的选项。如果没有超过指定行数,则不进行任何处理,如果超过了指定行数,则为最后一行文本添加“更多”文字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| func setReadMore(_ appendString: String, fitWidth: CGFloat) { let lineTexts = getLines(fitWidth: fitWidth) if lineTexts.count <= numberOfLines { isUserInteractionEnabled = false attributedText = NSAttributedString(string: text ?? "", attributes: attributes) return } var contentText = String() for i in 0..<numberOfLines { var lineText = lineTexts[i] if i == numberOfLines - 1 { lineText = trimmingTail(for: lineText, appendString: appendString, fitWidth: fitWidth) .trimmingCharacters(in: .newlines) } contentText.append(lineText) } let result = contentText + appendString let resultAttrString = NSMutableAttributedString(string: result) let range = NSMakeRange(0, resultAttrString.length) resultAttrString.addAttributes( attributes, range: range) let linkRange = NSMakeRange(resultAttrString.length - appendString.count, appendString.count) resultAttrString.addAttributes( [ .foregroundColor: UIColor.blue, ], range: linkRange) attributedText = resultAttrString }
|
这样基本的功能就已经实现了,只需要在填充文本之后调用 setReadMore
就可以实现显示更多的样式了。使用方式如下:
1 2 3
| label.numberOfLines = 5 label.text = "Some text content..." label.setReadMore("显示全文", fitWidth: 300)
|
如果你发现本文有任何疏漏错误,还请不吝指出,多谢。