Подтвердить что ты не робот

Как получить высоту для NSAttributedString с фиксированной шириной

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

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

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

  • Создание временного NSTextView и выполнение:
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

    Когда я запрашиваю рамку tmpView, ширина по-прежнему будет такой же, как и высота, и высота часто правильная... пока я не дойду до более длинных строк, когда это часто бывает вдвое меньшим, чем требуется. (Кажется, что максимальный размер не попадает: один кадр будет 273.0 (около 300 слишком короткий), другой будет 478.0 (только 60-ий слишком короткий)).

Я был бы признателен за любые указатели, если кому-то это удалось.

4b9b3361

Ответ 1

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

Ответ 2

-[NSAttributedString boundingRectWithSize:options:]

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

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

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

Использовать метод NSAttributedString

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

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

Ответ 6

В OS X 10.11+ для меня работает следующий метод (от Apple Calculate Text Height)

- (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;
}

Ответ 7

Swift 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

Ответ 8

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

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

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading`

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

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

Ответ 9

Ни один ответ на этой странице не работал у меня, и этот древний старый Objective-C код из документации Apple. То, что я, наконец, начал работать для UITextView, сначала устанавливает его свойство text или attributedText на нем, а затем вычисляет необходимый размер:

let size = textView.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.max))

Работает отлично. Booyah!

Ответ 10

Как много парней, упомянутых выше, и основывается на моем тесте.

Я использую open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect на iOS, как этот ниже:

let rect = attributedTitle.boundingRect(with: CGSize(width:200, height:0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)

Здесь 200 - фиксированная ширина как ожидаемая, высота, которую я даю ей 0, так как я думаю, что лучше сказать, что высота API неограничена.

Вариант здесь не так важен, я попробовал .usesLineFragmentOrigin или .usesLineFragmentOrigin.union(.usesFontLeading) или .usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics), он дал тот же результат.

И результат ожидается как мой.

Спасибо.

Ответ 11

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

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

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

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

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