web-dev-qa-db-ja.com

固定幅でNSAttributedStringの高さを取得する方法

固定幅のボックスでNSAttributedStringsの描画を行いたいのですが、描画時にそれらが占める正しい高さを計算するのに問題があります。これまでのところ、私は試しました:

  1. - (NSSize) sizeを呼び出しますが、文字列が必要な幅を提供するため、結果は(この目的のために)役に立たなくなります。

  2. 図面で使用しているとおりに、オプションでNSStringDrawingUsesLineFragmentOriginを必要な幅に整形したrectを使用して- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)optionsを呼び出します。結果は...理解するのが難しいです。確かに私が探しているものではありません。 ( this Cocoa-Devスレッドを含む多くの場所で指摘されているように)。

  3. 一時的なNSTextViewを作成して以下を実行します。
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

    tmpViewのフレームをクエリしても、幅は希望どおりであり、高さは多くの場合正しいです。必要なサイズの半分である長い文字列が見つかるまでです。 (ヒットしている最大サイズはないようです:1つのフレームは273.0の高さ(約300は短すぎます)、もう1つのフレームは478.0(60だけが短すぎます)になります)。

他の誰かがこれを管理してくれたなら、私はどんなポインタにも感謝します。

48
bonaldi

答えは使用する
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
しかし、渡すrectは、無制限にしたい次元に0.0がなければなりません(これは、完全に意味があります)。例 ここ

15
bonaldi
-[NSAttributedString boundingRectWithSize:options:]

NSStringDrawingUsesDeviceMetricsを指定して すべてのグリフ境界の結合 を取得できます。

-[NSAttributedString size]とは異なり、返されたNSRectは、文字列が描画された場合に変化する領域の寸法を表します。

@Bryanのコメントにあるように、 boundingRectWithSize:options: はOS X 10.11以降では非推奨(非推奨)です。これは、文字列のスタイル設定がコンテキストに応じて動的になるためです。

OS X 10.11以降については、Appleの テキストの高さの計算 開発者向けドキュメントを参照してください。

31
Graham Miln

Jerry Krinockのすばらしい(OS Xのみ) NS(Attributed)String+Geometrics カテゴリに興味があるかもしれません。

6
Rob Keniger

複数のフォントを持つ複雑な属性付き文字列があり、最初に試した上記の回答のいくつかで誤った結果が得られました。 UITextViewを使用すると適切な高さが得られましたが、ユースケース(コレクションセルのサイズ変更)には遅すぎました。私はSwift以前に参照され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
}
6
Matt R

スウィフト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
2
Josh O'Connor

OS X 10.11以降では、次の方法でうまくいきます(Appleの Calculation 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;
}
2
Erik

NSAttributedStringメソッドを使用する

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

サイズは領域の制約であり、計算された領域の幅は指定された幅に制限されますが、高さはこの幅に基づいて柔軟です。利用できない場合は、コンテキストにnilを指定できます。複数行のテキストサイズを取得するには、オプションにNSStringDrawingUsesLineFragmentOriginを使用します。

2
CodeBrew

私はこれに多くの時間を無駄にしただけなので、将来的に他の人を救うために追加の答えを提供します。グラハムの答えは90%正解ですが、重要な部分が1つ欠けています。

-boundingRectWithSize:options:で正確な結果を得るには次のオプションを渡す必要があります

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading`

LineFragmentOriginの1つを省略すると、意味がなくなります。返される四角形は高さが1行で、メソッドに渡すサイズはまったく考慮されません。

なぜこれがそれほど複雑で、あまり文書化されていないのかは、私には理解できません。しかし、そこにあります。これらのオプションを渡すと、完全に機能します(少なくともOS Xでは)。

1
Bryan

このページでの単一の答えは私にとってうまくいきませんでしたし、 Appleのドキュメント からのその古い古いObjective-Cコードもうまくいきませんでした。私が最終的にUITextViewで機能するようになったのは、最初にtextまたはattributedTextプロパティを設定してから、次のように必要なサイズを計算します。

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

完璧に動作します。ブイヤ!

0
Patrick Lynch

上記の多くの人として、そして私のテストに基づいています。

私は次のように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_は固定幅です予想どおり、height I it it APIの高さを教えてくださいnlimited

オプションはここではそれほど重要ではありません、_.usesLineFragmentOrigin_または.usesLineFragmentOrigin.union(.usesFontLeading)または.usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics)を試してみましたが、同じ結果が得られます。

そして結果は私の予想通りです。

ありがとう。

0
JerryZhou

attributedTextの高さと幅を見つけるためのヘルパークラスを見つけました(テスト済みコード)

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

上記のファイルをプロジェクトに追加します

使い方

let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
let width : CGFloat = attribString.calculatedTextWidth()
print("width is :: >> \(width)")
0
Hardik Thakkar

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)
0
ZAFAR007