Я хочу иметь возможность добавлять пользовательские подчеркивания к некоторым частям / словам только многострочного ярлыка.
Я пытался использовать NSAttributedString.Key.underlineStyle
с 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)")
И вот что я получаю:
frame: (24.0, 68.0, 366.0, 0.0) и прямоугольник: (34.0576171875, 0.030,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
последнего прямоугольника метки (572.2 ...) превышает его ширину (366), но яизо всех сил пытаясь понять, почему я получаю этот результат и как я могу улучшить свою функцию.
Спасибо за вашу помощь.