swift - Multiline UILabel:使用 boundingRect() 为标签的特定部分添加自定义下划线
问题描述
我希望能够为多行标签的某些部分/单词添加自定义下划线。
我尝试使用NSAttributedString.Key.underlineStyle
with a NSMutableAttributedString
,但我无法访问重要参数,例如文本和下划线之间的间距或下划线栏高度。
所以我正在考虑UILabel
在预期文本下方添加一个子层。这是我到目前为止所做的:
extension UILabel {
func underline(word: String, withColor color: UIColor) {
guard let text = self.text else {
return
}
if let range = text.range(of: word) {
let start = text.distance(from: text.startIndex, to: range.lowerBound)
let end = text.distance(from: text.startIndex, to: range.upperBound)
if let rect = self.boundingRectFor(characterRange: start..<end) {
let c = CALayer()
c.frame = CGRect(origin: CGPoint(x: rect.origin.x, y: rect.size.height + (self.font.pointSize / 8)), size: CGSize(width: rect.size.width, height: (self.font.pointSize / 4)))
c.backgroundColor = color.cgColor
self.layer.addSublayer(c)
}
}
}
private func boundingRectFor(characterRange: Range<Int>) -> CGRect? {
guard let attributedText = attributedText else { return nil }
let textStorage = NSTextStorage(attributedString: attributedText)
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: intrinsicContentSize)
textContainer.lineFragmentPadding = 0.0
layoutManager.addTextContainer(textContainer)
var glyphRange = NSRange()
layoutManager.characterRange(forGlyphRange: NSRange(location: characterRange.startIndex, length: characterRange.endIndex - characterRange.startIndex), actualGlyphRange: &glyphRange)
return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
}
}
boundingRectFor(characterRange)
是我发现的一种方法,它有望返回包含作为参数传递的范围中的子字符串的矩形。
underline(word: String, withColor color: UIColor)
将在标签文本中查找给定单词的第一次出现并在其下划线。
在视图控制器中的简单使用:
class ViewController: UIViewController {
@IBOutlet var labels: [UILabel]!
var texts: [String] = ["Lorem ipsum dolor sit amet.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."]
var textToUnderline = ["ipsum", "amet", "incididunt"]
var underliningColors: [UIColor] = [.red, .blue, .purple]
@IBOutlet weak var firstLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.labels.enumerated().forEach { index, label in
label.text = texts[index]
label.underline(word: textToUnderline[index], withColor: underliningColors[index])
}
}
}
这是结果的屏幕截图:
虽然这在第一行时效果很好,但在尝试为另一行下划线时我不再工作了。
为了更好地理解,我尝试在下面打印一些值if let rect
(在UILabel
扩展中):
// will print the label frame and the rect to underline
print("frame: \(self.frame) and rect: \(rect)")
这就是我得到的:
框架:(24.0、68.0、366.0、0.0)和矩形:(34.0576171875、0.0、30.3369140625、11.93359375)
框架:(24.0、100.0、366.0、0.0)和矩形:(176.35009765625、0.0、40.375、20.287109375)
框架:(24.0、124.0、366.0、0.0)和矩形:(572.20458984375、0.0、71.14013671875、17.900390625)
我意识到origin.x
最后一个标签 rect (572.2...) 的宽度超过了它的宽度 (366),但我很难理解为什么会得到这个结果以及如何改进我的功能。
谢谢您的帮助。
解决方案
你的整个方法都是错误的。使用文本视图,而不是标签,以便您可以访问完整的文本工具包堆栈(NSLayoutManager、NSTextStorage、NSTextContainer)。使用自定义布局管理器子类构造堆栈并覆盖drawBackground(forGlyphRange)
。
推荐阅读
- ffmpeg - 为音频 (AAC) MP4 轨道创建恒定的片段持续时间
- regex - 7 个字符的正则表达式,最多 2 个破折号
- mysql - MYSQL,如何将ID与同一张表中的其他列进行比较
- excel - 如何查询会话 cookie 并将其传递给 Excel PowerQuery?
- python - Django TrigramSimilarity 搜索没有提供这样的功能:SIMILARITY
- python - python中的计时器与烧瓶
- mysql - 如何在 Prisma 中添加时间属性?
- typescript - React-Native:什么会导致模块为空?
- python - httpx/httpcore 内脏中某处的异常
- .net - 登录在使用 .net 核心身份的身份验证中不起作用