Многострочная UILabel: добавление пользовательских подчеркиваний в определенные части метки с помощью boundingRect () - PullRequest
0 голосов
/ 13 июня 2019

Я хочу иметь возможность добавлять пользовательские подчеркивания к некоторым частям / словам только многострочного ярлыка.

Я пытался использовать 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])
        }
    }

}

А вот скриншот с результатом:

enter image description here

Хотя это работает нормально, когда речь идет о первой строке, я больше не работаю, когда пытаюсь подчеркнуть другую строку.

Чтобы лучше понять, я попытался напечатать некоторые значения ниже if let rectUILabel расширение):

// 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), но яизо всех сил пытаясь понять, почему я получаю этот результат и как я могу улучшить свою функцию.

Спасибо за вашу помощь.

1 Ответ

3 голосов
/ 17 июня 2019

Весь ваш подход неверен.Используйте текстовое представление, а не метку, чтобы иметь доступ к полному текстовому стеку (NSLayoutManager, NSTextStorage, NSTextContainer).Построить стек с помощью пользовательского подкласса менеджера компоновки и переопределить drawBackground(forGlyphRange).

...