NSString boundingRectWithSize слегка недооценивает правильную высоту - почему?

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

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

enter image description here

NSString *temp = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel felis nec massa ultricies blandit non id arcu. Sed enim est, facilisis a feugiat in, consectetur eget arcu. Aenean rutrum, lacus id euismod congue, nisl erat vulputate lectus, non faucibus tortor purus sed sem. Donec tempor dui egestas velit auctor vitae faucibus diam facilisis. Morbi convallis nulla quis est pulvinar quis ultricies sem sollicitudin.";

myText.stringValue = temp;

NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSFont systemFontOfSize:12], NSFontAttributeName,
                            [NSParagraphStyle defaultParagraphStyle], NSParagraphStyleAttributeName,
                            nil];

NSSize size = NSMakeSize(window.frame.size.width, 200);
myText.frame = [temp boundingRectWithSize:size options:NSLineBreakByWordWrapping | NSStringDrawingUsesLineFragmentOrigin attributes:attributes];

РЕДАКТИРОВАТЬ: Даже при ручном перемещении рамки ясно, что текст по-прежнему отключается. Измененный размер не совсем там. enter image description here

это относится к: NSString boundingRectWithSize слегка недооценивает правильную ширину - почему? а также NSTextView или NSTextField автоматически изменяет размеры рамки/рамки?

Ответ 1

У меня была та же проблема. Я нашел следующее в Документация:

Чтобы правильно рисовать и размер многострочного текста, пройдите NSStringDrawingUsesLineFragmentOrigin в параметре параметров.

Этот метод возвращает дробные размеры (в компоненте размера возвращается CGRect); чтобы использовать возвращаемый размер для просмотра размера, вы должны поднять его значение до ближайшего более высокого целого с использованием функции ceil.

Итак, это решило это для меня:

CGRect rect = [myLabel.text boundingRectWithSize:CGSizeMake(myLabel.frame.size.width, CGFLOAT_MAX)
                                                    options:NSStringDrawingUsesLineFragmentOrigin
                                                 attributes:@{NSFontAttributeName: myLabel.font}
                                                    context:nil];
rect.size.width = ceil(rect.size.width);
rect.size.height = ceil(rect.size.height);

Обновление (Swift 3)

Альтернативным способом для Wift 3 является:

let size = myLabel.text!.size(
  attributes: [NSFontAttributeName: myLabel.font]
)
let rect = CGSize(
  width: ceil(size.width),
  height: ceil(size.height)
)

Смотрите этот ответ для примера Objective-C.

Ответ 2

Это немного круглая, но я нашел, что запрос NSTextFieldCell дает гораздо более точные/надежные результаты высоты, чем использование [NSAttributedString boundingRectWithSize:options:].

Предполагая, что интересующая строка уже находится в NSTextField с соответствующими параметрами форматирования/обертывания, затем запросите текстовую ячейку, для какого размера она считает нужным соответствовать указанной ширине:

NSTextField* textField = ...

NSSize cellSize = [textField.cell cellSizeForBounds:NSMakeRect(0, 0, pathText.frame.size.width, CGFLOAT_MAX)];

cellSize.height дает высоту, которую текст фактически использует при рисовании.

Ответ 3

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

NSString *temp = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel felis nec massa ultricies blandit non id arcu. Sed enim est. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel felis nec massa ultricies blandit non id arcu. Sed enim est.";

myText.stringValue = temp;

NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSFont systemFontOfSize:12], NSFontAttributeName,
                            [NSParagraphStyle defaultParagraphStyle], NSParagraphStyleAttributeName,
                            nil];

NSSize size = NSMakeSize(window.frame.size.width, MAXFLOAT);


myText.frame = [temp boundingRectWithSize:size options:NSLineBreakByWordWrapping | NSStringDrawingUsesLineFragmentOrigin attributes:attributes];

// Keep current width but add some more on the bottom
NSSize tF = myText.frame.size;
[myText setFrameSize:NSMakeSize(tF.width, tF.height + 35)];

[myText setFrameOrigin:NSMakePoint((NSWidth(window.frame) - NSWidth(myText.frame)) / 2, 
                                   (NSHeight(window.frame) -  NSHeight(myText.frame) - 20))];

[myText setAutoresizingMask:NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin | NSViewMaxYMargin];

код >

Производит следующее:

enter image description here

Ответ 4

Я обнаружил, что NSString # boundingRectWithSize никогда не бывает точно правильным, единственное, что, кажется, работает хорошо, - это описанное здесь: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html

class func sizeForString(_ text : String, withFont font: NSFont, inWidth maxWidth: CGFloat) -> NSSize {
    let textStorage = NSTextStorage(string: text)
    let textContainer = NSTextContainer(containerSize: NSSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude))
    let layoutManager = NSLayoutManager()
    layoutManager.addTextContainer(textContainer)
    textStorage.addLayoutManager(layoutManager)
    textStorage.addAttribute(NSFontAttributeName, value: font, range: NSMakeRange(0, textStorage.length))
textContainer.lineFragmentPadding = 0
    layoutManager.glyphRange(for: textContainer)
    return layoutManager.usedRect(for: textContainer).size
 }