52

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

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

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

  3. 创建一个临时的 NSTextView 并做: 当我查询 tmpView 的框架时,宽度仍然是所需的,并且高度通常是正确的......直到我得到更长的字符串,当它通常是所需大小的一半时。(似乎没有达到最大尺寸:一帧将是 273.0 高(大约 300 太短),另一帧将是 478.0(只有 60-ish 太短))。
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

如果有其他人管理过这一点,我将不胜感激。

4

12 回答 12

30
-[NSAttributedString boundingRectWithSize:options:]

您可以指定NSStringDrawingUsesDeviceMetrics获取所有字形边界的并集

与 不同-[NSAttributedString size]的是,返回NSRect的表示如果绘制字符串会改变的区域的尺寸。

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

对于 OS X 10.11 及更高版本,请参阅 Apple 的计算文本高度开发人员文档。

于 2010-03-17T05:33:48.457 回答
14

答案使用
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
,但rect你传入的维度应该是 0.0,你希望是无限的(呃,这很有意义)。这里的例子。

于 2010-02-17T20:34:54.933 回答
12

我有一个具有多种字体的复杂属性字符串,并且在我首先尝试的上述一些答案中得到了不正确的结果。使用 UITextView 给了我正确的高度,但对于我的用例来说太慢了(调整集合单元的大小)。我使用之前引用并由 Erik描述的Apple 文档中描述的相同通用方法编写了 swift 代码。这给了我正确的结果,执行速度必须比让 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
}
于 2017-11-29T02:49:42.987 回答
5

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

于 2010-02-18T00:17:33.873 回答
3

在 OS X 10.11+ 上,以下方法适用于我(来自 Apple 的计算文本高度文档)

- (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;
}
于 2015-11-24T20:31:10.937 回答
3

斯威夫特 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)
于 2019-03-04T10:05:19.500 回答
1

使用 NSAttributedString 方法

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

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

于 2014-09-14T15:02:41.067 回答
1

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

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

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading

如果您省略 lineFragmentOrigin 之一,您将得到废话;返回的矩形将是单行高,并且根本不尊重您传递给方法的大小。

为什么这如此复杂且记录如此糟糕,这超出了我的理解。但是你现在有了。通过这些选项,它会完美运行(至少在 OS X 上)。

于 2015-09-15T02:16:34.120 回答
1

斯威夫特 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
于 2017-02-10T23:11:18.210 回答
1

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

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

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

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

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

结果和我一样。

谢谢。

于 2017-07-24T11:24:45.350 回答
0

此页面上没有一个答案对我有用, Apple 文档中古老的 Objective-C 代码也没有。我最终为 a 工作的UITextView是首先设置它的textorattributedText属性,然后计算所需的大小,如下所示:

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

完美运行。呸!

于 2016-04-21T05:09:23.383 回答
0

我找到了帮助类来查找属性文本的高度和宽度(测试代码)

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

在您的项目中添加上述文件

如何使用

let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
let width : CGFloat = attribString.calculatedTextWidth()
print("width is :: >> \(width)")
于 2018-05-25T10:49:05.413 回答