NSString boundingRectWithSize返回不必要的高度

时间:2014-01-22 00:31:33

标签: ios objective-c macos cocoa nsstring

使用[NSString boundingRectWithSize:options:attributes]时,返回的rect的大小比我对某些字符串的预期高。返回的高度似乎表示具有给定属性的字符串的最大可能高度,而不是字符串本身的高度。

假设相同的属性和选项,字符串“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}}

4 个答案:

答案 0 :(得分:27)

您可能希望在选项中使用NSStringDrawingUsesDeviceMetrics。来自docs

  

NSStringDrawingUsesDeviceMetrics

     

计算布局时使用图像字形边界(而不是印刷边界)。

答案 1 :(得分:5)

@omz仍然因为我的工作而受到赞誉。他的回答使我更加关注CoreText,因为我假设像boundingRectWithSize这样的东西最终会调用CoreText函数。

在WWDC 2012的Session 226中,有一整节专门用于计算一行的度量标准,令我惊讶的是,他们讨论了一个名为CTLineGetBoundsWithOptions的新CoreText函数。

据我所知,该方法仅记录在CTLine.hCoreText Changes文档中。在文档中进行常规搜索时,它肯定不会出现(对我而言)。

但它似乎有效,在我的测试中,它为我系统上安装的所有字体返回与boundingRectWithSize完全相同的结果。更好的是,它似乎比boundingRectWithSize快了近2倍。

正如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);

答案 2 :(得分:0)

我尝试了boundingRectWithSize功能,但它对我没用。

工作是sizeThatFits

用法:

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

CGSize size = [myLabel sizeThatFits:maxSize];

答案 3 :(得分:0)

基于omz's answer的Swift 3+解决方案:

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