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

NSString boundingRectWithSize возвращает ненужную высокую высоту

При использовании [NSString boundingRectWithSize:options:attributes] размер возвращаемого прямоугольника выше, чем я ожидал бы для определенных строк. Возвращаемая высота представляет собой максимально возможную высоту строки с указанными атрибутами, а не высоту самой строки.

Предполагая те же атрибуты и параметры, высота, возвращаемая для строки "cars", равна той же высоте, что и для строки "ÉTAS-UNIS" (обратите внимание на акцент на E).

Я бы ожидал, что boundingRectWithSize рассмотрит только символы в данной строке, которые, на мой взгляд, вернут более короткую высоту для строки "cars".

В прикрепленных скриншотах я заполнил прямоугольник, возвращенный из boundingRectWithSize, и обозначил красным то, что я предположил бы, что ограничивающий прямоугольник должен был быть. Ширина прямоугольника в значительной степени, как я ожидал бы, но высота значительно выше, чем я ожидал. Почему это?

Bounding Rect Example

Пример кода:

NSRect boundingRect = NSZeroRect;
NSSize constraintSize = NSMakeSize(CGFLOAT_MAX, 0);

NSString *lowercaseString = @"cars";
NSString *uppercaseString = @"ÉTAS-UNIS";
NSString *capitalizedString = @"Japan";

NSFont *drawingFont = [NSFont fontWithName:@"Georgia" size:24.0];
NSDictionary *attributes = @{NSFontAttributeName : drawingFont, NSForegroundColorAttributeName : [NSColor blackColor]};

boundingRect = [lowercaseString boundingRectWithSize:constraintSize options:0 attributes:attributes];
NSLog(@"Lowercase rect: %@", NSStringFromRect(boundingRect));

boundingRect = [uppercaseString boundingRectWithSize:constraintSize options:0 attributes:attributes];
NSLog(@"Uppercase rect: %@", NSStringFromRect(boundingRect));

boundingRect = [capitalizedString boundingRectWithSize:constraintSize options:0 attributes:attributes];
NSLog(@"Capitalized rect: %@", NSStringFromRect(boundingRect));

Выход:

Lowercase rect: {{0, -6}, {43.1953125, 33}}
Uppercase rect: {{0, -6}, {128.44921875, 33}}
Capitalized rect: {{0, -6}, {64.5, 33}}
4b9b3361

Ответ 1

Возможно, вы захотите использовать NSStringDrawingUsesDeviceMetrics в параметрах. Из docs:

NSStringDrawingUsesDeviceMetrics

Используйте границы глифов изображения (вместо типографских границ) при расчете макета.

Ответ 2

@omz все еще получает кредит за выполнение этой работы для меня. Его ответ заставил меня еще раз взглянуть на CoreText, так как я предполагаю, что что-то вроде boundingRectWithSize в конечном итоге вызывает функцию CoreText.

В сеансе 226 от WWDC 2012 был целый раздел, посвященный вычислению метрик линии, и, к моему удивлению, они рассказали о новой функции CoreText под названием CTLineGetBoundsWithOptions.

Насколько я могу судить, этот метод документируется только в CTLine.h и в документе CoreText Changes. Это, конечно, не возникает (для меня) при обычном поиске в документации.

Но, похоже, он работает, и в моем тестировании он возвращает тот же результат, что и boundingRectWithSize для всех шрифтов, установленных в моей системе. Еще лучше, что он почти в 2 раза быстрее, чем boundingRectWithSize.

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

Грубый пример кода:

NSFont *font = [NSFont systemFontOfSize:13.0];
NSDictionary *attributes = @{(__bridge NSString *)kCTFontAttributeName : font};
NSAttributedString *attributeString = [[NSAttributedString alloc] initWithString:self.text attributes:attributes];

CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)attributeString);
CGRect bounds = CTLineGetBoundsWithOptions(line, kCTLineBoundsUseGlyphPathBounds);

CFRelease(line);

return NSRectFromCGRect(bounds);

Ответ 3

Я попробовал функцию boundingRectWithSize, но для меня это не сработало.

Что работало sizeThatFits

Использование:

CGSize maxSize = CGSizeMake(myLabel.frame.size.width, CGFLOAT_MAX)

CGSize size = [myLabel sizeThatFits:maxSize];

Ответ 4

Быстрое решение 3+, основанное на ответе omz:

let string: NSString = "test"
let maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: .greatestFiniteMagnitude)
let boundingRect = string.boundingRect(with: maxSize, options: .usesDeviceMetrics, attributes: <string attributes>)