NSAttributedString boundingRect возвращает неправильную высоту - PullRequest
0 голосов
/ 03 февраля 2019

Я вычисляю высоту NSAttributedString следующим образом.

let maxSize = NSSize(width: w, height: CGFloat.greatestFiniteMagnitude)
let rect = boundingRect(with: maxSize, options: [.usesFontLeading, .usesLineFragmentOrigin])
let height = rect.integral.size.height

Я пробовал каждый "взлом", который упоминался в SO, но высота строки становится более неточной, чем меньше ширина (расчетная высота больше, чем фактическая высота).

Согласно другим сообщениям следующие проблемы вызывают вычисление размера:

  • Пробел
  • Нет цвета переднего плана
  • Нет цвета фона
  • Ширина ниже 300 не работает (?)

Я обнаружил, что ни одно из этих предложений не имеет никакого значения.Приписываемая строка представляет собой объединение строк, каждая из которых имеет foregroundColor и backgroundColor.Строка также имеет следующий стиль абзаца:

 let pstyle = NSMutableParagraphStyle()
 pstyle.lineSpacing = 6
 pstyle.lineBreakMode = .byWordWrapping

и userFixedPitchFont размера 11.

Почему ошибка высоты увеличивается с уменьшением ширины?

PS: я полагаю, что это как-то связано с lineBreak, так как ошибка увеличивается, если больше строк переносится по словам.

1 Ответ

0 голосов
/ 03 февраля 2019

Я обнаружил, что это решение работает.Обратите внимание, что если вы не установите lineFragmentPadding на 0, он выдаст те же (неправильные) результаты, что и boundingRect.

extension NSAttributedString {

    func sizeFittingWidth(_ w: CGFloat) -> CGSize {
        let textStorage = NSTextStorage(attributedString: self)
        let size = CGSize(width: w, height: CGFloat.greatestFiniteMagnitude)
        let boundingRect = CGRect(origin: .zero, size: size)

        let textContainer = NSTextContainer(size: size)
        textContainer.lineFragmentPadding = 0

        let layoutManager = NSLayoutManager()
        layoutManager.addTextContainer(textContainer)

        textStorage.addLayoutManager(layoutManager)

        layoutManager.glyphRange(forBoundingRect: boundingRect, in: textContainer)

        let rect = layoutManager.usedRect(for: textContainer)

        return rect.integral.size
    }
}
...