如何以固定宽度获取NSAttributedString的高度

时间:2010-02-17 16:53:07

标签: cocoa nstextview appkit nsattributedstring

我想在固定宽度的框中绘制一些NSAttributedStrings,但是在计算绘制它们时会占用的正确高度时遇到问题。到目前为止,我已经尝试过:

  1. 调用- (NSSize) size,但结果没用(为此目的),因为它们会给出字符串所需的宽度。

  2. 使用形状为我想要的矩形调用- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options,并在选项中调用NSStringDrawingUsesLineFragmentOrigin,就像我在绘图中使用的一样。结果......难以理解;当然不是我想要的。 (正如许多地方所指出的那样,包括this Cocoa-Dev线程)。

  3. 创建临时NSTextView并执行:
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

    当我查询tmpView的框架时,宽度仍然是所希望的,并且高度通常是正确的......直到我得到更长的字符串,当它通常是所需尺寸的一半时。 (似乎没有最大尺寸被击中:一帧高273.0(约300太短),另一帧将是478.0(只有60太短))。

  4. 如果有其他人管理过这个,我会感激不尽。

12 个答案:

答案 0 :(得分:31)

-[NSAttributedString boundingRectWithSize:options:]

您可以指定NSStringDrawingUsesDeviceMetrics来获取union of all glyph bounds

-[NSAttributedString size]不同,返回的NSRect表示在绘制字符串时会更改的区域的尺寸。

作为@Bryan的评论,在OS X 10.11及更高版本中不推荐使用boundingRectWithSize:options:(不推荐)。这是因为字符串样式现在是动态的,具体取决于上下文。

对于OS X 10.11及更高版本,请参阅Apple的Calculating Text Height开发人员文档。

答案 1 :(得分:15)

答案 使用
 - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
但是你传入的rect应该在你想要无限制的维度中有0.0(呃,这是完全合理的)。示例here

答案 2 :(得分:6)

您可能对Jerry Krinock的伟大(仅限OS X)NS(Attributed)String+Geometrics类别感兴趣,该类别旨在进行各种字符串测量,包括您正在寻找的内容。

答案 3 :(得分:6)

我有一个带有多种字体的复杂属性字符串,并且我首先尝试了一些上述答案,结果不正确。使用UITextView给了我正确的高度,但对于我的用例来说太慢了(调整收集单元格)。我使用先前引用并由Erik描述的Apple doc中描述的相同通用方法编写了快速代码。这给了我正确的结果,必须比UITextView更快地执行计算。

private func heightForString(_ str : NSAttributedString, width : CGFloat) -> CGFloat {
    let ts = NSTextStorage(attributedString: str)

    let size = CGSize(width:width, height:CGFloat.greatestFiniteMagnitude)

    let tc = NSTextContainer(size: size)
    tc.lineFragmentPadding = 0.0

    let lm = NSLayoutManager()
    lm.addTextContainer(tc)

    ts.addLayoutManager(lm)
    lm.glyphRange(forBoundingRect: CGRect(origin: .zero, size: size), in: tc)

    let rect = lm.usedRect(for: tc)

    return rect.integral.size.height
}

答案 4 :(得分:2)

使用NSAttributedString方法

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context

大小是区域的约束,计算的区域宽度限制为指定的宽度,而高度基于此宽度是灵活的。如果不可用,可以为上下文指定nil。要获得多行文本大小,请使用NSStringDrawingUsesLineFragmentOrigin作为选项。

答案 5 :(得分:2)

在OS X 10.11+上,以下方法适用于我(来自Apple的Calculating Text Height文档)

- (CGFloat)heightForString:(NSAttributedString *)myString atWidth:(float)myWidth
{
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:myString];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:
        NSMakeSize(myWidth, FLT_MAX)];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];
    [layoutManager glyphRangeForTextContainer:textContainer];
    return [layoutManager
        usedRectForTextContainer:textContainer].size.height;
}

答案 6 :(得分:2)

斯威夫特3:

let attributedStringToMeasure = NSAttributedString(string: textView.text, attributes: [
        NSFontAttributeName: UIFont(name: "GothamPro-Light", size: 15)!,
        NSForegroundColorAttributeName: ClickUpConstants.defaultBlackColor
])

let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: widthOfActualTextView, height: 10))
placeholderTextView.attributedText = attributedStringToMeasure
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))

height = size.height

这个答案对我来说非常有用,不像其他那些给我更大的字符串不正确的高度。

如果您想使用常规文本而不是归因文本来执行此操作,请执行以下操作:

let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: ClickUpConstants.screenWidth - 30.0, height: 10))
placeholderTextView.text = "Some text"
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))

height = size.height

答案 7 :(得分:1)

我只是浪费了很多时间,所以我提供了一个额外的答案,以便将来拯救其他人。格雷厄姆的回答是正确的90%,但缺少一个关键部分:

要使用-boundingRectWithSize:options: 获得准确的结果,您必须通过以下选项

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading`

如果省略lineFragmentOrigin,你会得到废话;返回的rect将是一行高,并且根本不会考虑您传递给方法的大小。

为什么这么复杂,如此缺乏文件记录超出我的范围。但是你现在有了。通过这些选项,它将完美地工作(至少在OS X上)。

答案 8 :(得分:0)

此页面上没有一个答案适合我,Apple's documentation也没有那个古老的Objective-C代码。我最终为UITextView工作的是首先在其上设置textattributedText属性,然后像这样计算所需的大小:

let size = textView.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.max))

完美无缺。 Booyah!

答案 9 :(得分:0)

正如上面提到的很多人,并且基于我的测试。

我在iOS上使用open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect,如下所示:

let rect = attributedTitle.boundingRect(with: CGSize(width:200, height:0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)

此处 200是固定宽度作为您的预期,高度我给它0 ,因为我认为最好是告诉API高度无限

选项在这里不是那么重要,我尝试.usesLineFragmentOrigin.usesLineFragmentOrigin.union(.usesFontLeading).usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics),它会给出相同的结果。

结果是我的预期。

感谢。

答案 10 :(得分:0)

我找到帮助类来查找attributedText的高度和宽度(测试代码)

https://gist.github.com/azimin/aa1a79aefa1cec031152fa63401d2292

在项目中添加上述文件

如何使用

function requestRange(start, end) {
  function request(i) {
    if (i >= end) return;
    let data = "i=" + i;
    let ajax = $.ajax({
      url: '/ajax.php',
      type: 'post',
      data: data
    });
    ajax.done(function (result) {
      console.log(result);
      request(i + 1);
    }); 
  }
  request(start);
}

requestRange(0, 5);

答案 11 :(得分:0)

Swift 4.2

let attributedString = self.textView.attributedText
let rect = attributedString?.boundingRect(with: CGSize(width: self.textView.frame.width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
print("attributedString Height = ",rect?.height)