* Точно * вычисление высоты текста в Cocoa (для Mac, а не iOS)

Как я могу вычислить высоту строки в пределах определенной метки (NSTextField) для некоторой заданной фиксированной ширины?

Я погуглил разные методы и попробовал этот метод от Apple. Это работает, за исключением того, что высота становится на одну строку слишком короткой для более длинных строк. Таким образом, по-видимому, имеются некоторые неточности в настоящих способах.

У меня также была та же проблема с другим кодом, который я гуглил.

Есть ли у кого-нибудь здесь точный код для определения высоты текста (с учетом конкретной ширины)?

Ответ 1

Я решил проблему, используя категорию NS (Attributed) String + Geometrics, представленную в http://www.sheepsystems.com/sourceCode/sourceStringGeometrics.html. В заголовочном файле этой категории есть хорошее объяснение того, как это работает. В частности, они упоминают, что для NSTextView вместо NSTextField следует использовать NSTextView, потому что практически невозможно получить точную высоту для последнего.

Таким образом, я заменил NSTextField на NSTextView. Во-первых, я сделал свой NSTextView похожим на свой NSTextField с помощью этого кода:

[textView setEditable:YES];
[textView insertText:someString];
[textView setFont:[NSFont fontWithName:@"Lucida Grande"
   size:11] range:NSMakeRange(0,[someString length])];
[textView setEditable:NO];
[textView setSelectable:NO];

Затем я получил высоту с помощью этого кода:

NSDictionary *attributes
  = [NSDictionary dictionaryWithObjectsAndKeys:
     [textView font],NSFontAttributeName,nil];
NSAttributedString *attributedString
  = [[NSAttributedString alloc] initWithString:
     [textView string] attributes:attributes];
CGFloat height = [attributedString heightForWidth:
                 [textView frame].size.width];

Высота действительно оказывается точной для NSTextView.

Возможно, я опустил некоторые мелкие детали, но вы получите картину. Я надеюсь, что это поможет кому-то там.

Ответ 2

(решение в Swift находится в конце сообщения)

Я пробовал все эти методы, и никто не работал у меня. "Официальный" способ (как упоминалось в OP) в большинстве случаев отсутствовал для моего NSTextField. Переключение на NSTextView было невозможно из-за того, что XCode/IB всегда сбой после добавления "NSTextView в NSScrollView".

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

CGFloat minHeight = [((NSTextFieldCell *)[yourTextField cell]) cellSizeForBounds:NSMakeRect(0, 0, YOUR_MAX_WIDTH, FLT_MAX)].height;

Этот код работал для каждого отдельного теста, который не сработал с другими методами. Я надеюсь, что это помогает кому-то. Это всегда заставляет меня чувствовать себя таким глупым, когда я беру 2 часа, чтобы найти решение, казалось бы, легкой проблемы.

SLIGHT EDIT. При ближайшем рассмотрении это работает, только если вы прочитали stringValue NSTextField, прежде чем я попрошу ячейку рассчитать ее высоту. Поэтому перед фактическим вычислением я добавил следующую бессмысленную строку:

[yourTextField setStringValue:yourTextField.stringValue];

Я знаю, что это действительно очень плохо, но на данный момент мне все равно. Я просто хочу надежное решение. Не стесняйтесь понизить меня или еще лучше: предлагайте лучшее решение.


Решение в Swift (спасибо iphaaw за то, что привлекли это мое внимание!)

myTextField.cell!.cellSizeForBounds(NSMakeRect(CGFloat(0.0), CGFloat(0.0), width, CGFloat(FLT_MAX))).height

Ответ 3

Спасибо, действительно, Ифау и Энчилада за это действительно крутое маленькое решение. Я нашел, что он работал так хорошо при установке высоты строк и т.д., Что я превратил iphaaw Swift в пару расширений NSTextField. Наслаждайтесь

extension NSTextField {
func bestheight(text: String, width: CGFloat) -> CGFloat {

    self.stringValue = text
    let getnumber = self.cell!.cellSizeForBounds(NSMakeRect(CGFloat(0.0), CGFloat(0.0), width, CGFloat(FLT_MAX))).height

    return getnumber
   }
}

extension NSTextField {
func bestwidth(text: String, height: CGFloat) -> CGFloat {

    self.stringValue = text
    let getnumber = self.cell!.cellSizeForBounds(NSMakeRect(CGFloat(0.0), CGFloat(0.0), CGFloat(FLT_MAX), height)).width

    return getnumber
   }
}

Я надеюсь, что кто-то найдет их полезными. рассматривает KGH

Ответ 5

Ответ на энергичный кодинг работает так хорошо! Это действительно плохо? Кажется, он работает отлично. Наконец, потратив много часов на поиск других решений.

Вот мой Swift перевод кода:

myTextField.cell!.cellSizeForBounds(NSMakeRect(CGFloat(0.0), CGFloat(0.0), width, CGFloat(FLT_MAX))).height

Пожалуйста, не голосуйте за этот ответ. Вместо этого активируйте запись сильного кодирования.

Ответ 6

Это ответ ArtbyKGH для Swift 4+:

extension NSTextField {
    func bestHeight(for text: String, width: CGFloat) -> CGFloat {
        stringValue = text
        let height = cell!.cellSize(forBounds: NSRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude)).height

        return height
    }

    func bestWidth(for text: String, height: CGFloat) -> CGFloat {
        stringValue = text
        let width = cell!.cellSize(forBounds: NSRect(x: 0, y: 0, width: .greatestFiniteMagnitude, height: height)).width

        return width
    }
}