NSAttributedStringのboundingRectWithSizeが誤ったサイズを返す


151

属性付き文字列の四角形を取得しようとしていますが、boundingRectWithSize呼び出しは渡したサイズを考慮せず、高さが大きい(長い文字列である)のではなく、1行の高さの四角形を返します。高さの非常に大きな値と、以下のコードのように0を渡して実験しましたが、返される四角形は常に同じです。

CGRect paragraphRect = [attributedText boundingRectWithSize:CGSizeMake(300,0.0)
  options:NSStringDrawingUsesDeviceMetrics
  context:nil];

これは壊れていますか、それともラップされたテキストの四角形を返すために何か他のことをする必要がありますか?


5
あなたはたまたま切り捨て/クリッピングのある段落スタイルを持っていlineBreakModeますか?
danyowdee

1
UILabelが不正な幅に測定/折り返すためにこれを読んでいる場合は、stackoverflow.com / questions / 46200027 /…をご覧ください。具体的には、アプリの起動時にNSAllowsDefaultLineBreakStrategyをfalseに設定します。
エリック

回答:


312

正しいオプションを提供していないようです。ラベルをラッピングするには、少なくとも次の情報を提供します。

CGRect paragraphRect =
  [attributedText boundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)
  options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
  context:nil];

注:元のテキストの幅が300.f未満の場合、行の折り返しは行われないため、バインドされたサイズが正しいことを確認してください。そうしないと、間違った結果が得られます。


25
それが機能するので、誰もがこの答えを見る必要があります。おそらく、このメソッドと属性付き文字列一般には、より明確なドキュメントが必要です。どこを見る必要があるかは明らかではありません。
macserv 2013年

31
注意!常に機能するとは限りません!返される幅は、サイズパラメータの幅よりも大きい場合があります。Applesのメソッドのドキュメントにも、「サイズパラメータで指定する制約は、文字列のサイズを設定するレンダラーのガイドです。ただし、追加のスペースが必要な場合、このメソッドによって返される実際の外接する四角形は、制約よりも大きくなる場合があります。文字列全体をレンダリングします。」
Klaas 2013

75
NSAttributedStringとboundingRectWithSizeのAPIは衝撃的です。
Malhal 2013

27
もう1つの問題は、paragraphRectで返される高さ(およびおそらく幅)がほとんど常に小数値であることです。なので高さとして141.3と出てくるかもしれません。ceilf(paragraphRect.size.height)切り上げられるようにからの結果を使用する必要があります。私はいつもこれを忘れて、なぜ私のラベルがまだクリッピングされているのか疑問に思っています。
jamone 2014年

39
私は常にboundingRectWithSize:CGRectIntegral()で計算をラップCGRectIntegral rounds the rectangle’s origin downward and its size upward to the nearest whole integersします。この場合、高さと幅が小数値の場合にクリッピングが発生しないように、高さと幅を切り上げます。
runmad 2014年

47

何らかの理由で、boundingRectWithSizeは常に間違ったサイズを返します。私は解決策を見つけました。テキストセットの適切なサイズを返すUItextView -sizeThatFitsのメソッドがあります。そのため、boundingRectWithSizeを使用する代わりに、ランダムなフレームでUITextViewを作成し、それぞれの幅とCGFLOAT_MAXの高さでそのsizeThatFitsを呼び出します。適切な高さになるサイズを返します。

   UITextView *view=[[UITextView alloc] initWithFrame:CGRectMake(0, 0, width, 10)];   
   view.text=text;
   CGSize size=[view sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
   height=size.height; 

whileループでサイズを計算する場合は、自動リリースプールにサイズを追加することを忘れないでください。UITextViewがn個作成されるため、autoreleasepoolを使用しない場合、アプリのランタイムメモリが増加します。


1
これは機能します。私が試した他の方法では、仕事に行くことができませんでした。私の問題は、割り当てていた属性付き文字列の複雑さに起因すると思います。これはRTFファイルから来ており、さまざまなフォント、行間隔、シャドウまで使用しており、UsesLineFragmentOriginおよびUsesFontLeadingを使用しても、十分に高い結果を得ることができず、属性付き文字列が長くなるほど問題が最悪でした。私の推測では、比較的単純な文字列の場合、boundingRectWithSizeメソッド機能する可能性があります。彼らは本当にこれを使ったより良い方法を必要としています、imo。
John Bushnell

メソッドが呼び出されるのとUITextView同じ時間を作成するのはやり過ぎだと思いませんheightForRowAtIndexPathか?それは時間がかかる
ヤーノシュ

@Jánosに同意しますが、これが私にとって有効な唯一の解決策でした。
shoan 2015

ところで、私は適切な解決策を求めて:stackoverflow.com/questions/32495744/...
ヤーノシュ

これが機能する唯一のソリューションです。この問題は9年あり、Appleは明らかにこれを解決したくないか、エンジニアがこれに対する解決策を見つけるのに苦労しています。ここではiOS 11について話していますが、同じバグがまだ残っています。
Duck

31

Ed McManusは確かにこれを機能させるための鍵を提供しています。うまくいかないケースを見つけた

UIFont *font = ...
UIColor *color = ...
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                     font, NSFontAttributeName,
                                     color, NSForegroundColorAttributeName,
                                     nil];

NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString: someString attributes:attributesDictionary];

[string appendAttributedString: [[NSAttributedString alloc] initWithString: anotherString];

CGRect rect = [string boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];

rectの高さが正しくありません。(stringに追加される)anotherStringが属性ディクショナリなしで初期化されたことに注意してください。これは、anotherStringの正当な初期化子ですが、この場合、boundingRectWithSize:は正確なサイズを提供しません。


33
ありがとうございました!boundingRectWithSizeを実際に機能させたい場合は、NSAttributedStringのすべての部分に、少なくともNSFontAttributeNameとNSForegroundColorAttributeNameが設定された辞書セットが必要であることがわかります。どこにも文書化されていないようです。
ベンウィーラー

3
また、boundingRectWithSizeが正しく機能するためには、組み合わされて単一のNSAttributedStringsが同じもの(つまり同じフォント)を使用する必要があることに注意してください。
Ben Wheeler

1
このソリューションは、UILabelカテゴリで「縮小して合わせる」ために使用するソリューションと非常に似ています。これを機能させるための鍵は、FLT_MAXの高さで境界矩形を作成することでした。それがないと、1行だけの長方形が表示されます。
dre1138 2013

すべてのラウンドで+1。これで本当にうまくいきました。
Wex

私はこの問題を抱えていました。NSMutableAttributedString属性なしでスペースと改行を使用しましたが、サイズが間違っていました。
vbezhenar 2016年

28

長い調査の後の私の最終的な決定:
- boundingRectWithSize関数は文字の途切れのないシーケンスに対してのみ正しいサイズを返します!文字列にスペースまたは何かが含まれている場合(Appleの「グリフの一部」によって呼び出されます)-テキストを表示するために必要なrectの実際のサイズを取得することは不可能です!
文字列のスペースを文字に置き換えたところ、すぐに正しい結果が得られました。

アップルはここに言っています:https : //developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

「このメソッドは、文字列内のグリフの実際の境界を返します。一部のグリフ(スペースなど)は、渡されたサイズによって指定されたレイアウト制約とオーバーラップできるため、場合によっては、サイズコンポーネントの幅の値が返さCGRectれるサイズパラメータの幅の値を超える可能性があります。」

したがって、実際の四角形を計算する別の方法を見つける必要があります...


長い調査の後、最終的に解決策が見つかりました!!! に関連するすべてのケースでうまく機能するかどうかはわかりませんUITextViewが、重要な重要なものが検出されました!

boundingRectWithSize関数だけでなくCTFramesetterSuggestFrameSizeWithConstraints(および他の多くのメソッド)は、正しい長方形を使用すると、サイズとテキスト部分を正しく計算します。例えば- UITextView有し、textView.bounds.size.width-この値ではないテキストが上に描画するときに、システムによって使用される実際の矩形UITextView

私は非常に興味深いパラメータを見つけ、コードで簡単な計算を実行しました:

CGFloat padding = textView.textContainer.lineFragmentPadding;  
CGFloat  actualPageWidth = textView.bounds.size.width - padding * 2;

そして、魔法の作品-私のテキストはすべて正しく計算されました!楽しい!


1
うん、私も気づいた、それは幅の制約を維持せず、常に大きくなる。それは本当にクールではありません、彼らは働きの方法を廃止しました、そして今私たちはこのがらくたに対処しなければなりません。
ボリス・ガフロフ2014

1
あなたはサー、私の新しいヒーローです!これは私を夢中にさせていました。私はtextContainersインセットを設定しているので、textView.bounds.size.witdhの代わりにtextView.textContainer.size.widthを使用する必要がありました。
GCBenson 2016年

私はこれを十分に賛成投票できませんでした。境界計算でスペースが考慮されないのは奇妙です。
Chase Holland

1
スペースを「3」などのダミー文字に置き換えると、正確な高さが返されます
Abuzar Amin

1
これは私のキャリアとQAチームとの関係を救いました。私はあなたにビール男を借りています!!
lucaslt89

14

Swift 4バージョン

let string = "A great test string."
let font = UIFont.systemFont(ofSize: 14)
let attributes: [NSAttributedStringKey: Any] = [.font: font]
let attributedString = NSAttributedString(string: string, attributes: attributes)
let largestSize = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)

//Option one (best option)
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
let textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRange(), nil, largestSize, nil)

//Option two
let textSize = (string as NSString).boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], attributes: attributes, context: nil).size

//Option three
let textSize = attributedString.boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], context: nil).size

CTFramesetterを使用したテキストの測定は、整数サイズを提供し、絵文字やその他のUnicode文字を適切に処理するため、最適に機能します。


10

私はこれらの提案のいずれにも運がありませんでした。私の文字列にはUnicodeの箇条書きが含まれており、それらが計算で悲しみを引き起こしていたと思われます。UITextViewが描画を適切に処理していることに気付いたので、その計算を活用するためにそれを見ました。私は次のことを行いました。これはおそらくNSString描画メソッドほど最適ではありませんが、少なくともそれは正確です。また、を呼び出すためだけにUITextViewを初期化するよりも少し最適です-sizeThatFits:

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(width, CGFLOAT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:formattedString];
[textStorage addLayoutManager:layoutManager];

const CGFloat formattedStringHeight = ceilf([layoutManager usedRectForTextContainer:textContainer].size.height);

Unicodeドットではなかった可能性があります。文字列の末尾にスペースがあった可能性があります。上記の回答をご覧ください:stackoverflow.com/a/25941139/337934
SamB

9

尻尾を切り詰めて境界ボックスを取得したい場合は、この質問が役立ちます。

CGFloat maxTitleWidth = 200;

NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;

NSDictionary *attributes = @{NSFontAttributeName : self.textLabel.font,
                             NSParagraphStyleAttributeName: paragraph};

CGRect box = [self.textLabel.text
              boundingRectWithSize:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)
              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
              attributes:attributes context:nil];

9

boundingRectWithSizeを実際に機能させたい場合は、NSAttributedStringのすべての部分に、少なくともNSFontAttributeNameとNSForegroundColorAttributeNameが設定された辞書セットが必要であることがわかります。

どこにも文書化されていないようです。


おかげで、これは私にとっての解決策でしたが、NSFongroundAttributeNameだけが必要で、NSForegroundColorAttributeNameは必要ないことがわかりました
Marmoy

7

@warrenmフレームセッターの方法は私にはうまくいきませんでした。

これを取得しました。この関数は、特定のWidthに対してiphone / Ipad SDKのNSAttributedStringの文字列範囲に必要なフレームサイズを決定するのに役立ちます。

UITableView Cellsの動的な高さに使用できます

- (CGSize)frameSizeForAttributedString:(NSAttributedString *)attributedString
{
    CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CGFloat width = YOUR_FIXED_WIDTH;

    CFIndex offset = 0, length;
    CGFloat y = 0;
    do {
        length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
        CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));

        CGFloat ascent, descent, leading;
        CTLineGetTypographicBounds(line, &ascent, &descent, &leading);

        CFRelease(line);

        offset += length;
        y += ascent + descent + leading;
    } while (offset < [attributedString length]);

    CFRelease(typesetter);

    return CGSizeMake(width, ceil(y));
}

HADDAD ISSAのおかげで>>> http://haddadissa.blogspot.in/2010/09/compute-needed-heigh-for-fixed-width-of.html


どのデバイス(iPhone 4および5)でも動作しません。iOS7.1.2を搭載したデバイスとiOS8.3を搭載した4sシミュレータの両方:(
sanjana

これにはインポートが必要ですか?
jose920405 2016

@KaranAlangatはい#import <CoreText/CoreText.h>
jose920405 2016

7

推奨されるソリューションは改行を処理しないことがわかりました。

私はこのアプローチがすべてのケースで機能することを発見しました:

UILabel* dummyLabel = [UILabel new];
[dummyLabel setFrame:CGRectMake(0, 0, desiredWidth, CGFLOAT_MAX)];
dummyLabel.numberOfLines = 0;
[dummyLabel setLineBreakMode:NSLineBreakByWordWrapping];
dummyLabel.attributedText = myString;
[dummyLabel sizeToFit];
CGSize requiredSize = dummyLabel.frame.size;

私の場合、バックグラウンドスレッドで計算を行う必要があり、UI要素の使用は許可されていません
Lubbo

4

これらのテクニックを使用しても正確なサイズが得られないという同じ問題があり、それを機能させるためにアプローチを変更しました。

長い属性文字列があり、それが切り詰められずに適切に表示されるように、スクロールビューに収めようとしています。テキストを確実に機能させるために私が行ったのは、高さを制約としてまったく設定せず、代わりに固有のサイズを引き継ぐことでした。これで、テキストは切り捨てられることなく正しく表示され、高さを計算する必要がなくなりました。

高さを確実に取得する必要がある場合は、非表示のビューとこれらの制約を作成し、制約が適用されたらフレームの高さを取得するとします。


2
これを示すコードサンプルを追加できますか?どうも。
Nathan Buggia 14

3

ゲームに少し遅れましたが、Finderでファイルを編集するのと同じように、属性付き文字列の周囲に収まる境界ボックスを見つけてフォーカスリングを作成する方法を見つけようとしています。文字列の末尾にスペースがある場合、または文字列内に複数のスペースがある場合、私が試したすべてが失敗しました。boundingRectWithSizeこれと同様に惨めに失敗しますCTFramesetterCreateWithAttributedString

NSLayoutManager次のコードを使用すると、私がこれまでに見つけたすべてのケースでトリックを実行し、文字列を完全にバインドする四角形を返します。おまけ:テキストを選択すると、選択範囲の端が、返された四角形の境界まで移動します。以下のコードは、からのlayoutManagerを使用していNSTextViewます。

NSLayoutManager* layout = [self layoutManager];
NSTextContainer* container = [self textContainer];

CGRect focusRingFrame = [layout boundingRectForGlyphRange:NSMakeRange(0, [[self textStorage] length]) inTextContainer:container];

2
textView.textContainerInset = UIEdgeInsetsZero;
NSString *string = @"Some string";
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:12.0f], NSForegroundColorAttributeName:[UIColor blackColor]};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[textView setAttributedText:attributedString];
CGRect textViewFrame = [textView.attributedText boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.view.frame)-8.0f, 9999.0f) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];
NSLog(@"%f", ceilf(textViewFrame.size.height));

すべてのフォントで完璧に動作します!


1

同じ問題がありましたが、制約された高さが正しく設定されていることがわかりました。だから私は次のことをしました:

-(CGSize)MaxHeighForTextInRow:(NSString *)RowText width:(float)UITextviewWidth {

    CGSize constrainedSize = CGSizeMake(UITextviewWidth, CGFLOAT_MAX);

    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [UIFont fontWithName:@"HelveticaNeue" size:11.0], NSFontAttributeName,
                                          nil];

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:RowText attributes:attributesDictionary];

    CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];

    if (requiredHeight.size.width > UITextviewWidth) {
        requiredHeight = CGRectMake(0, 0, UITextviewWidth, requiredHeight.size.height);
    }

    return requiredHeight.size;
}

1
    NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [UIFont systemFontOfSize:18], NSFontAttributeName,
                                      [UIColor blackColor], NSForegroundColorAttributeName,
                                      nil];

    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myLabel.text attributes:stringAttributes];
    myLabel.attributedText = attributedString; //this is the key!

    CGSize maximumLabelSize = CGSizeMake (screenRect.size.width - 40, CGFLOAT_MAX);

    CGRect newRect = [myLabel.text boundingRectWithSize:maximumLabelSize
                                                       options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                    attributes:stringAttributes context:nil];

    self.myLabelHeightConstraint.constant = ceilf(newRect.size.height);

このページのすべてを試しましたが、正しくフォーマットされていないUILabelのケースが1つありました。実際にラベルにattributedTextを設定すると、問題が最終的に修正されました。


1

私が気づいていたことの1つは、元の四角形(CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)contextが、渡した幅よりも広いことです。これが発生すると、文字列が切り捨てられます。私はそれを次のように解決しました:

NSString *aLongString = ...
NSInteger width = //some width;            
UIFont *font = //your font;
CGRect rect = [aLongString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
                                        options:(NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin)
                                     attributes:@{ NSFontAttributeName : font,
                                                   NSForegroundColorAttributeName : [UIColor whiteColor]}
                                        context:nil];

if(rect.size.width > width)
{
    return rect.size.height + font.lineHeight;
}
return rect.size.height;

もう少しコンテキストについて。複数行のテキストがあり、それを表示するのに適切な高さを見つけようとしていました。boundRectWithSizeは、指定した幅よりも大きい幅を返すことがあったため、過去の幅と計算された高さを使用してテキストを表示すると、切り捨てられます。boundingRectWithSizeが間違った幅を使用したときのテストから、高さを短くする量は1行でした。したがって、幅が大きいかどうかを確認し、大きい場合は、フォントのlineHeightを追加して、切り捨てを回避するのに十分なスペースを提供します。


行の高さは幅にどのように影響しますか?
Roi Mulia、

@RoiMulia行の高さは幅に影響しません。私はこれが私のバグをどのように修正したかについてより多くのコンテキストを提供するために私の答えを更新しました。
オディス、

0
    NSAttributedString *attributedText =[[[NSAttributedString alloc]
                                          initWithString:joyMeComment.content
                                          attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:TextFont]}] autorelease];

    CGRect paragraphRect =
    [attributedText boundingRectWithSize:CGSizeMake(kWith, CGFLOAT_MAX)
                                 options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                 context:nil];
    contentSize = paragraphRect.size;

    contentSize.size.height+=10;
    label.frame=contentSize;

ラベルのフレームが10を追加しない場合、このメソッドは機能しません。これがあなたに役立つことを願っています!ググ運。


0
Add Following methods in ur code for getting correct size of attribute string 
1.
    - (CGFloat)findHeightForText:(NSAttributedString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font
 {
    UITextView *textView = [[UITextView alloc] init];
    [textView setAttributedText:text];
    [textView setFont:font];
    CGSize size = [textView sizeThatFits:CGSizeMake(widthValue, FLT_MAX)];
    return size.height;

}

2. Call on heightForRowAtIndexPath method
     int h = [self findHeightForText:attrString havingWidth:yourScreenWidth andFont:urFont];

私の場合、バックグラウンドスレッドで計算を行う必要があり、UI要素の使用は許可されていません
Lubbo

0

まったく同じ問題があったので、考えを追加したいと思います。

私はUITextViewそれがより良いテキスト配置を持っていたので使用していました(正当化、現時点ではで利用できませんでしたUILabel)が、non-interactive-non-scrollableを「シミュレート」するためにUILabel、完全なスクロール、バウンス、およびユーザー操作をオフにします。

もちろん、問題はテキストが動的であり、幅が固定されていても、新しいテキスト値を設定するたびに高さが再計算されることでした。

boundingRectWithSize私が見ることができるものから、私にはまったくうまくいきませんでした、カウントに含まれないUITextViewマージンを上に追加boundingRectWithSizeしていたため、取得されboundingRectWithSizeた高さが本来よりも低くなりました。

テキストは迅速に更新されることはなかったため、2〜3秒ごとに最も頻繁に更新される可能性のあるいくつかの情報に使用されるだけなので、次のアプローチを決定しました。

/* This f is nested in a custom UIView-inherited class that is built using xib file */
-(void) setTextAndAutoSize:(NSString*)text inTextView:(UITextView*)tv
{
    CGFloat msgWidth = tv.frame.size.width; // get target's width

    // Make "test" UITextView to calculate correct size
    UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, msgWidth, 300)]; // we set some height, really doesn't matter, just put some value like this one.
    // Set all font and text related parameters to be exact as the ones in targeted text view
    [temp setFont:tv.font];
    [temp setTextAlignment:tv.textAlignment];
    [temp setTextColor:tv.textColor];
    [temp setText:text];

    // Ask for size that fits :P
    CGSize tv_size = [temp sizeThatFits:CGSizeMake(msgWidth, 300)];

    // kill this "test" UITextView, it's purpose is over
    [temp release];
    temp = nil;

    // apply calculated size. if calcualted width differs, I choose to ignore it anyway and use only height because I want to have width absolutely fixed to designed value
    tv.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, msgWidth, tv_size.height );
}

*上記のコードは私のソースから直接コピーされていません。この記事に必要のない他の多くのものか​​ら調整/クリアする必要がありました。コピー、貼り付け、およびそれが機能する作業コードに使用しないでください。

明らかな欠点は、呼び出しごとにallocとreleaseがあることです。

しかし、利点は、あなたがboundingRectWithSizeは、テキストや計算を描画する方法間の互換性に応じて、避けることであるのサイズとにおけるテキスト描画の実装UITextView(またはUILabelもあなただけ置き換える使用することができますUITextViewUILabel)。Appleが持つ可能性のある「バグ」は、この方法で回避されます。

PSそれはあなたがこの「臨時雇用者」UITextViewを必要とするべきではないようでsizeThatFits、ターゲットから直接尋ねることができるように思われますが、それは私にとってはうまくいきませんでした。ロジックはそれが機能するはずであると言いますが、一時のalloc / releaseはUITextView必要ありませんが、機能しませんでした。しかし、この解決策は、私が設定するすべてのテキストに対して完璧に機能しました。


私の場合、バックグラウンドスレッドで計算を行う必要があり、UI要素の使用は許可されていません
Lubbo

0

わかりましたので、これをデバッグするのに多くの時間を費やしました。私がテキストのboundingRectWithSize表示を許可することによって定義された最大テキスト高さUITextViewが、フレームサイズよりも低いことがわかりました。

私の場合、フレームは最大140ptですが、UITextViewは最大131ptのテキストを許容します。

私はそれを手動で把握し、「実際の」最大の高さをハードコーディングする必要がありました。

これが私の解決策です:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSString *proposedText = [textView.text stringByReplacingCharactersInRange:range withString:text];
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:proposedText];
    CGRect boundingRect;
    CGFloat maxFontSize = 100;
    CGFloat minFontSize = 30;
    CGFloat fontSize = maxFontSize + 1;
    BOOL fit;
    NSLog(@"Trying text: \"%@\"", proposedText);
    do {
        fontSize -= 1;
        //XXX Seems like trailing whitespaces count for 0. find a workaround
        [attributedText addAttribute:NSFontAttributeName value:[textView.font fontWithSize:fontSize] range:NSMakeRange(0, attributedText.length)];
        CGFloat padding = textView.textContainer.lineFragmentPadding;
        CGSize boundingSize = CGSizeMake(textView.frame.size.width - padding * 2, CGFLOAT_MAX);
        boundingRect = [attributedText boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil];
        NSLog(@"bounding rect for font %f is %@; (max is %f %f). Padding: %f", fontSize, NSStringFromCGRect(boundingRect), textView.frame.size.width, 148.0, padding);
        fit =  boundingRect.size.height <= 131;
    } while (!fit && fontSize > minFontSize);
    if (fit) {
        self.textView.font = [self.textView.font fontWithSize:fontSize];
        NSLog(@"Fit!");
    } else {
        NSLog(@"No fit");
    }
    return fit;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.