Как определить пользовательский NSUnderlineStyle

Глядя на документацию для NSLayoutManager в частности, drawUnderlineForGlyphRange: underlineType: baselineOffset: lineFragmentRect: lineFragmentGlyphRange: containerOrigin: метод, я заметил следующее (выделено мое):

underlineVal
Стиль подчеркивания для рисования. Это значение является маской, полученной из значения для NSUnderlineStyleAttributeName -например, (NSUnderlinePatternDash | NSUnderlineStyleThick). Подклассы могут определять пользовательские стили подчеркивания.

Мой вопрос: как именно это должно быть сделано?

NSUnderlineStyle это перечисление, которое вы не можете расширить или переопределить. Вы можете, конечно, предоставить случайное сырье Int значение для атрибута, не охватываемое перечислением:

self.addAttribute(NSUnderlineStyleAttributeName, value: 100022, range: lastUpdatedWordRange)

Который доставит "недействительный", но пригодный для использования underlineType в Менеджере макетов:

скриншот отладчика

Но это вряд ли кажется безопасным и определенно не элегантным.

Я не смог найти ни одного примера в Интернете или дополнительной информации в документации Apple о том, как выглядят эти мифические пользовательские стили подчеркивания. Я хотел бы знать, если я упускаю что-то очевидное.

1 ответ

У меня есть пример проекта, который я использовал для выступления по TextKit, который я дал некоторое время назад и который делает именно то, что вы ищете: https://github.com/dtweston/text-kit-example

Подчеркивание в этом случае - волнистая линия:

подчеркнуть скриншот

Основой решения является пользовательский NSLayoutManager:

let CustomUnderlineStyle = 0x11

class UnderlineLayoutManager: NSLayoutManager {

    func drawFancyUnderlineForRect(_ rect: CGRect) {
        let left = rect.minX
        let bottom = rect.maxY
        let width = rect.width

        let path = UIBezierPath()
        path.move(to: CGPoint(x: left, y: bottom))
        var x = left
        var y = bottom
        var i = 0
        while (x <= left + width) {
            path.addLine(to: CGPoint(x: x, y: y))
            x += 2
            if i % 2 == 0 {
                y = bottom + 2.0
            }
            else {
                y = bottom
            }
            i += 1;
        }

        path.stroke()
    }

    override func drawUnderline(forGlyphRange glyphRange: NSRange, underlineType underlineVal: NSUnderlineStyle, baselineOffset: CGFloat, lineFragmentRect lineRect: CGRect, lineFragmentGlyphRange lineGlyphRange: NSRange, containerOrigin: CGPoint) {

        if underlineVal.rawValue & CustomUnderlineStyle == CustomUnderlineStyle {

            let charRange = characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil)
            if let underlineColor = textStorage?.attribute(NSUnderlineColorAttributeName, at: charRange.location, effectiveRange: nil) as? UIColor {
                underlineColor.setStroke()
            }

            if let container = textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil) {
                let boundingRect = self.boundingRect(forGlyphRange: glyphRange, in: container)
                let offsetRect = boundingRect.offsetBy(dx: containerOrigin.x, dy: containerOrigin.y)

                drawFancyUnderlineForRect(offsetRect)
            }
        }
        else {
            super.drawUnderline(forGlyphRange: glyphRange, underlineType: underlineVal, baselineOffset: baselineOffset, lineFragmentRect: lineRect, lineFragmentGlyphRange: lineGlyphRange, containerOrigin: containerOrigin)
        }
    }
}
Другие вопросы по тегам