Как получить высоту для NSAttributedString при фиксированной ширине - PullRequest
48 голосов
/ 17 февраля 2010

Я хочу сделать несколько рисунков NSAttributedStrings в полях фиксированной ширины, но у меня возникают проблемы с вычислением правильной высоты, которую они будут поднимать при рисовании. Пока что пробовал:

  1. Вызов - (NSSize) size, но результаты бесполезны (для этой цели), так как они дадут любую ширину, какую пожелает строка.

  2. Вызов - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options с прямоугольником в нужную ширину и NSStringDrawingUsesLineFragmentOrigin в опциях, точно так же, как я использую в своем чертеже. Результаты ... трудно понять; конечно не то, что я ищу. (Как указано в ряде мест, включая эту ветку Cocoa-Dev).

  3. Создание временного NSTextView и выполнение:
    [[tmpView textStorage] setAttributedString:aString];<br> [tmpView setHorizontallyResizable:NO];<br> [tmpView sizeToFit];
    Когда я запрашиваю фрейм tmpView, ширина остается желаемой, а высота часто правильной ... пока я не получу более длинные строки, когда это часто вдвое меньше требуемого размера. (Похоже, что максимальный размер не ударил: один кадр будет иметь высоту 273,0 (около 300 слишком коротко), а другой - 478,0 (только 60-кратный слишком короткий)).

Буду признателен за любые указатели, если бы кто-то еще справился с этим.

Ответы [ 12 ]

31 голосов
/ 17 марта 2010
-[NSAttributedString boundingRectWithSize:options:]

Вы можете указать NSStringDrawingUsesDeviceMetrics, чтобы получить объединение всех границ глифов .

В отличие от -[NSAttributedString size], возвращаемое NSRect представляет размеры области, которая изменится, если будет нарисована строка.

Как отмечает @Bryan, boundingRectWithSize:options: не рекомендуется (не рекомендуется) в OS X 10.11 и более поздних версиях. Это потому, что стилизация строк теперь динамическая в зависимости от контекста.

Для OS X 10.11 и более поздних версий см. Документацию Apple Расчет высоты текста для разработчиков.

15 голосов
/ 17 февраля 2010

Ответ - для использования
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
но rect, который вы передаете, должен иметь 0.0 в измерении, которое вы хотите быть неограниченным (что, конечно, имеет смысл). Пример здесь .

6 голосов
/ 29 ноября 2017

У меня сложная атрибутивная строка с несколькими шрифтами, и я получил неправильные результаты с несколькими из приведенных выше ответов, которые я попробовал первым. Использование UITextView дало мне правильную высоту, но было слишком медленным для моего варианта использования (определение размера ячеек коллекции). Я написал быстрый код, используя тот же общий подход, описанный в Apple doc , упомянутом ранее и описанном Эриком. Это дало мне правильные результаты с более быстрым выполнением, чем выполнение вычисления с помощью UITextView.

private func heightForString(_ str : NSAttributedString, width : CGFloat) -> CGFloat {
    let ts = NSTextStorage(attributedString: str)

    let size = CGSize(width:width, height:CGFloat.greatestFiniteMagnitude)

    let tc = NSTextContainer(size: size)
    tc.lineFragmentPadding = 0.0

    let lm = NSLayoutManager()
    lm.addTextContainer(tc)

    ts.addLayoutManager(lm)
    lm.glyphRange(forBoundingRect: CGRect(origin: .zero, size: size), in: tc)

    let rect = lm.usedRect(for: tc)

    return rect.integral.size.height
}
6 голосов
/ 18 февраля 2010

Вас может заинтересовать замечательная категория Джерри Кринока (только для OS X) NS(Attributed)String+Geometrics, предназначенная для измерения всех типов струн, включая то, что вы ищете.

2 голосов
/ 11 февраля 2017

Свифт 3:

let attributedStringToMeasure = NSAttributedString(string: textView.text, attributes: [
        NSFontAttributeName: UIFont(name: "GothamPro-Light", size: 15)!,
        NSForegroundColorAttributeName: ClickUpConstants.defaultBlackColor
])

let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: widthOfActualTextView, height: 10))
placeholderTextView.attributedText = attributedStringToMeasure
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))

height = size.height

Этот ответ отлично работает для меня, в отличие от других, которые давали мне неверные высоты для больших строк.

Если вы хотите сделать это с обычным текстом вместо атрибутивного текста, сделайте следующее:

let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: ClickUpConstants.screenWidth - 30.0, height: 10))
placeholderTextView.text = "Some text"
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))

height = size.height
2 голосов
/ 24 ноября 2015

В OS X 10.11+ у меня работает следующий метод (из документа Apple Расчет высоты текста )

- (CGFloat)heightForString:(NSAttributedString *)myString atWidth:(float)myWidth
{
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:myString];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:
        NSMakeSize(myWidth, FLT_MAX)];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];
    [layoutManager glyphRangeForTextContainer:textContainer];
    return [layoutManager
        usedRectForTextContainer:textContainer].size.height;
}
2 голосов
/ 14 сентября 2014

Используйте метод NSAttributedString

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context

Размер является ограничением для области, вычисленная ширина области ограничена указанной шириной, тогда как высота является гибкой на основе этой ширины.Можно указать ноль для контекста, если это не доступно.Чтобы получить размер текста в несколько строк, используйте NSStringDrawingUsesLineFragmentOrigin для параметров.

1 голос
/ 15 сентября 2015

Я просто потратил кучу времени на это, поэтому я даю дополнительный ответ, чтобы спасти других в будущем. Ответ Грэма верен на 90%, но ему не хватает одного ключевого элемента:

Чтобы получить точные результаты с -boundingRectWithSize:options: , вы ДОЛЖНЫ передать следующие параметры :

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading`

Если вы опустите строку lineFragmentOrigin, вы получите ерунду обратно; возвращаемый прямоугольник будет иметь высоту в одну строку и вообще не будет соответствовать размеру, который вы передаете в метод.

Почему это так сложно и так плохо документировано, мне не под силу. Но у вас есть это. Передайте эти параметры, и они будут отлично работать (по крайней мере, на OS X).

0 голосов
/ 04 марта 2019

Swift 4,2

let attributedString = self.textView.attributedText
let rect = attributedString?.boundingRect(with: CGSize(width: self.textView.frame.width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
print("attributedString Height = ",rect?.height)
0 голосов
/ 25 мая 2018

Я нашел вспомогательный класс, чтобы найти высоту и ширину приписанного текста (проверенный код)

https://gist.github.com/azimin/aa1a79aefa1cec031152fa63401d2292

Добавьте вышеуказанный файл в ваш проект

Как использовать

let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
let width : CGFloat = attribString.calculatedTextWidth()
print("width is :: >> \(width)")
...