计算文本容器的高度

Calculate height of the text container

为了在 NSTableView 中显示视图,我必须计算 NSTextField 中的文本。我正在使用这种方法:

float heightForStringDrawing(NSString *myString, NSFont *myFont,float myWidth) {
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:myString];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth, FLT_MAX)];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];
    [textStorage addAttribute:NSFontAttributeName value:myFont
                        range:NSMakeRange(0, [textStorage length])];


    (void) [layoutManager glyphRangeForTextContainer:textContainer];
    return [layoutManager usedRectForTextContainer:textContainer].size.height;
}

如果是线衬,效果很好,但是当我有不止一条线时,我的视图高度的确切宽度就太小了。我一直在寻找几个小时的答案,但我仍然一无所获。这是屏幕截图:

由于填充,我将 10 添加到函数的结果中。正如您在短消息中看到的那样,它效果很好,它被裁剪的时间更长。感谢您的帮助。

您没有显示调用 heightForStringDrawing() 函数的代码。你提供的宽度是多少?如果它是文本字段的宽度,那就错了,因为不允许文本字段中的文本一直延伸到文本字段的边缘。也有水平填充。

无论如何,还有更直接的方法。创建一个与您要测量的对象的属性相匹配的文本字段。设置其 stringValuefont。将其 preferredMaxLayoutWidth 设置为可用宽度。 (请注意,这是对齐矩形,而不是框架矩形。如果需要转换,请使用 -alignmentRectForFrame:。)现在,询问它的 intrinsicSize。然后,您必须使用 -frameForAlignmentRect:.

将其从对齐矩形转换为框架矩形

根据您的情况,您可以通过存档和取消存档来复制现有的文本字段,或者您可以加载包含它的 NIB 来获取它。


获得具有必要特征的文本字段的一种方法是克隆现有的文本字段:

NSData* data = [NSKeyedArchiver archivedDataWithRootObject:_label];
NSTextField* clone = [NSKeyedUnarchiver unarchiveObjectWithData:data];

要计算适合给定宽度所需的框架高度:

NSRect rect = { NSZeroPoint, NSMakeSize(desiredWidth, 100 /* arbitrary */) };
rect = [clone alignmentRectForFrame:rect];
clone.preferredMaxLayoutWidth = NSWidth(rect);
rect.size = clone.intrinsicContentSize;
rect = [clone frameForAlignmentRect:rect];
CGFloat resultingHeight = NSHeight(rect);

现在,在您的屏幕截图中,行高必须高于文本字段高度,因为那里有边距。多高取决于您的布局。由于您在评论中提到了约束,因此您可能正在使用自动布局。在那种情况下,边距(约束距离)实际上将应用于文本字段的对齐矩形。因此,您将跳过倒数第二行代码。也就是说,行高将是文本字段的固有高度(未调整)加上您的约束强加的边距。

为了避免很多这样的东西摆弄边距,您或许应该处理与您的 table 单元格视图相匹配的完整视图层次结构。因此,您将直接加载 table 单元格视图 NIB。您将适当地设置文本字段的内容(以及您的单元格视图的子视图需要的任何其他内容)。您将对单元格视图本身应用宽度约束以模拟列宽。然后,您会向单元格视图询问其 fittingSize。让自动布局系统根据内容计算整体高度,包括边距和您的约束所隐含的所有内容。

当然,这仅在文本字段使用 preferredMaxLayoutWidth 时有效。否则,它不会响应包装而增加其固有高度。由于您不想对首选宽度或边距大小进行硬编码,因此您可以执行两次。在单元格视图中设置控件的内容并添加宽度约束后,调用 -layoutSubtreeIfNeeded。然后,查询文本字段的框架,将其转换为对齐矩形,将 preferredMaxLayoutWidth 设置为对齐宽度,然后才查询单元格视图的 fittingSize.

对我来说完美的解决方案:

// creating temporary cell just to take height from it later
NSTextFieldCell *tmp = [NSTextFieldCell new];
// settings proper values from out NSTextField which we want to check height for
[tmp setStringValue:s];
[tmp setFont:font];
NSRect rect;
rect.size.height=FLT_MAX;
// width which we want to check height for
// in my case 100 (width of nstextfield), -4 (minus left and right margin)
rect.size.width=100-4; 
// getting needed height
h = [tmp cellSizeForBounds:rect].height;