非推奨のsizeWithFontの代替:iOS 7で?


回答:


521

sizeWithAttributes:代わりに使用してくださいNSDictionary。次のUITextAttributeFontように、キーとフォントオブジェクトのペアを渡します。

CGSize size = [string sizeWithAttributes:
    @{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];

// Values are fractional -- you should take the ceilf to get equivalent values
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));

60
an NSStringとaで作業UILabelする場合(常にそうではないが、多くの場合そうです)、コードの重複などを防ぐために[UIFont systemFontOfSize:17.0f]label.font-で置き換えることもできます-既存のデータを参照するのではなく、複数回入力するか、全体にわたって定数を参照することにより、コードのメンテナンスに役立ちます場所など
toblerpwn 2013年

8
@toblerpwnラベルが存在せず、理論上のラベルを計算しようとしている可能性があります。
ボットボット2013

5
この例を使用して、固定幅、たとえば250のラベルが付いたsize.heightを取得するにはどうすればよいですか?または、autolatyout witchのラベルが幅の割合を占めており、landscapemodeに移動した場合。
Pedroinpeace 2013

12
@Pedroinpeace boundingRectWithSize:options:attributes:context:代わりに使用しCGSizeMake(250.0f, CGFLOAT_MAX)、ほとんどの場合渡されます。
James Kuang

9
sizeWithAttributes:に注意すべき他の何かは100%同等ではありません。サイズを整数(ピクセル)値に切り上げるために使用されるsizeWithFont。結果の高さ/幅にceilfを使用することをお勧めします。位置計算に使用すると、ぼやけたアーティファクト(esp non retina HW)が発生する可能性があります。
Nick H247 14

172

その一連のNSString+UIKit関数(sizewithFont:...など)はUIStringDrawingライブラリに基づいていたため、スレッドセーフではなかったため、この関数は廃止されたと思います。他のUIKit機能と同様に、メインスレッド以外で実行しようとすると、予期しない動作が発生します。特に、関数を複数のスレッドで同時に実行した場合、アプリがクラッシュする可能性があります。これがiOS 6でのboundingRectWithSize:...メソッドを導入した理由ですNSAttributedString。これはNSStringDrawingライブラリの上に構築され、スレッドセーフです。

新しいNSString boundingRectWithSize:...関数を見ると、と同じ方法で属性配列を要求しますNSAttributeString。推測しなければならないのですが、NSStringiOS 7のこの新しい関数はNSAttributeString、iOS 6の関数のラッパーにすぎません。

あなたが唯一のiOS 6とiOS 7をサポートしている場合、そのノートで、私は間違いなくあなたのすべてを変えるだろうNSString sizeWithFont:...NSAttributeString boundingRectWithSize。奇妙なマルチスレッドコーナーケースが発生した場合に、頭痛の種を大幅に節約できます。これが私が変換した方法ですNSString sizeWithFont:constrainedToSize:

以前は何でしたか:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font 
               constrainedToSize:(CGSize){width, CGFLOAT_MAX}];

と置き換えることができます:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;

ドキュメントの言及に注意してください:

iOS 7以降では、このメソッドは(返されるのサイズコンポーネントでCGRect)小数サイズを返します。返されたサイズを使用してビューのサイズを変更するには、ceil関数を使用して、その値を最も近い整数に上げる必要があります。

したがって、ビューのサイズ変更に使用される計算された高さまたは幅を引き出すには、次のようにします。

CGFloat height = ceilf(size.height);
CGFloat width  = ceilf(size.width);

boundingRectWithSizeはiOS 6.0では非推奨です。
2013年

2
@Nirav非推奨についての言及はありません。非推奨と表示されている場所を教えていただけますか?ありがとう!(developer.apple.com/library/ios/documentation/uikit/reference/...
ミスターT

2
たぶん@NiravはiOS 6のNSStringで利用できないことを意味していました(回答で間接的に言及されています)?
newenglander 2013年

1
@VagueExplanation私はそれを試したところ、boundingRectForSizeはNSAttributedStringではまだ非推奨ではありません。また、ドキュメンテーションで非推奨とは言われていません。
T氏

5
ceilf()の使用について言及するのは素晴らしい。他のどこにもそれが記載されておらず、私のサイズは常に少し小さすぎました。ありがとう!
ジョナサンブラウン

29

sizeWithFontApple Developerサイトで確認できるように、これは非推奨であるため、使用する必要がありますsizeWithAttributes

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

NSString *text = @"Hello iOS 7.0";
if (SYSTEM_VERSION_LESS_THAN(@"7.0")) {
    // code here for iOS 5.0,6.0 and so on
    CGSize fontSize = [text sizeWithFont:[UIFont fontWithName:@"Helvetica" 
                                                         size:12]];
} else {
    // code here for iOS 7.0
   CGSize fontSize = [text sizeWithAttributes: 
                            @{NSFontAttributeName: 
                              [UIFont fontWithName:@"Helvetica" size:12]}];
}

17
より良いユースにこの場合は[NSObject respondsToSelector:]、ここのような方法:stackoverflow.com/a/3863039/1226304
derpoliuk

16

この問題を処理するためのカテゴリを作成しました。

#import "NSString+StringSizeWithFont.h"

@implementation NSString (StringSizeWithFont)

- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}

このようにして、検索/置換sizeWithFont:を行うsizeWithMyFont:だけで済みます。


1
ただし、これにより、sizeWithFontを参照するためにios7 +でコンパイラ警告が生成されるか、sizeWithAttributesを参照するために<ios7でコンパイル警告/エラーが生成されます。おそらく、respondsToSelectorをチェックする代わりにマクロを使用するのが最善です-必要な場合。しかし、ioS6 SDKを使用してアップルに配信することはもうできないので...おそらくそうではありません!
Nick H247 14

これを追加して、警告を抑制します#pragma GCC診断は「-Wdeprecated-declarations」を無視しました
Ryan

10

iOS7では、tableview:heightForRowAtIndexPathメソッドの正しい高さを返すロジックが必要でしたが、sizeWithAttributesは、固定幅のテーブルセルに配置されることがわからないため、文字列の長さに関係なく常に同じ高さを返します。これは私にとってはうまくいき、テーブルのセルの幅を考慮して正しい高さを計算しました!これは、上記のT氏の回答に基づいています。

NSString *text = @"The text that I want to wrap in a table cell."

CGFloat width = tableView.frame.size.width - 15 - 30 - 15;  //tableView width - left border width - accessory indicator - right border width
UIFont *font = [UIFont systemFontOfSize:17];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;
size.height = ceilf(size.height);
size.width  = ceilf(size.width);
return size.height + 15;  //Add a little more padding for big thumbs and the detailText label

7

動的な高さを使用する複数行のラベルでは、サイズを適切に設定するために追加情報が必要になる場合があります。UIFontおよびNSParagraphStyleでsizeWithAttributesを使用して、フォントと改行モードの両方を指定できます。

段落スタイルを定義し、次のようなNSDictionaryを使用します。

// set paragraph style
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:NSLineBreakByWordWrapping];
// make dictionary of attributes with paragraph style
NSDictionary *sizeAttributes        = @{NSFontAttributeName:myLabel.font, NSParagraphStyleAttributeName: style};
// get the CGSize
CGSize adjustedSize = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);

// alternatively you can also get a CGRect to determine height
CGRect rect = [myLabel.text boundingRectWithSize:adjustedSize
                                                         options:NSStringDrawingUsesLineFragmentOrigin
                                                      attributes:sizeAttributes
                                                         context:nil];

高さを探している場合は、CGSize 'adjustedSize'またはCGRectをrect.size.heightプロパティとして使用できます。

NSParagraphStyleの詳細については、https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.htmlをご覧ください。


1
AdjustedSizeの直後に構文の問題があるようです。
Chris Prince

その構文の問題、クリスキャッチしてくれてありがとう
bitsand

6
// max size constraint
CGSize maximumLabelSize = CGSizeMake(184, FLT_MAX)

// font
UIFont *font = [UIFont fontWithName:TRADE_GOTHIC_REGULAR size:20.0f];

// set paragraph style
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;

// dictionary of attributes
NSDictionary *attributes = @{NSFontAttributeName:font,
                             NSParagraphStyleAttributeName: paragraphStyle.copy};

CGRect textRect = [string boundingRectWithSize: maximumLabelSize
                                     options:NSStringDrawingUsesLineFragmentOrigin
                                  attributes:attributes
                                     context:nil];

CGSize expectedLabelSize = CGSizeMake(ceil(textRect.size.width), ceil(textRect.size.height));

1
これにより、頭痛の時間を節約できました。Kirit氏に感謝します。CGRectブロックの前に属性と辞書を定義することで、私にとって何ができたのかがわかりました。
Azin Mehrnoosh、2018

3

UILabelインスタンスを受け取る関数を作成します。CGSizeを返します

CGSize constraint = CGSizeMake(label.frame.size.width , 2000.0);
// Adjust according to requirement

CGSize size;
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){

    NSRange range = NSMakeRange(0, [label.attributedText length]);

    NSDictionary *attributes = [label.attributedText attributesAtIndex:0 effectiveRange:&range];
    CGSize boundingBox = [label.text boundingRectWithSize:constraint options: NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;

    size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
}
else{
    size = [label.text sizeWithFont:label.font constrainedToSize:constraint lineBreakMode:label.lineBreakMode];
}

return size;

私は動的な行の高さの場合、HeightForRowメソッドを完全に削除し、UITableViewAutomaticDimensionを使用しました。tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Eugene Braginets

3

代替ソリューション-

CGSize expectedLabelSize;
if ([subTitle respondsToSelector:@selector(sizeWithAttributes:)])
{
    expectedLabelSize = [subTitle sizeWithAttributes:@{NSFontAttributeName:subTitleLabel.font}];
}else{
    expectedLabelSize = [subTitle sizeWithFont:subTitleLabel.font constrainedToSize:subTitleLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping];
}

1
これにより、iOS6と7の両方でコンパイラの警告が生成されます。また、それぞれの動作はまったく異なります。
Nick H247 14

3

@bitsandを基にして、これはNSString + Extrasカテゴリに追加したばかりの新しいメソッドです。

- (CGRect) boundingRectWithFont:(UIFont *) font constrainedToSize:(CGSize) constraintSize lineBreakMode:(NSLineBreakMode) lineBreakMode;
{
    // set paragraph style
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    [style setLineBreakMode:lineBreakMode];

    // make dictionary of attributes with paragraph style
    NSDictionary *sizeAttributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName: style};

    CGRect frame = [self boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:sizeAttributes context:nil];

    /*
    // OLD
    CGSize stringSize = [self sizeWithFont:font
                              constrainedToSize:constraintSize
                                  lineBreakMode:lineBreakMode];
    // OLD
    */

    return frame;
}

結果のフレームのサイズをそのまま使用します。


2

引き続きご利用いただけますsizeWithFont。ただし、iOS> = 7.0では、文字列に先頭と末尾のスペースまたは終了行が含まれていると、メソッドがクラッシュします\n

使用する前にテキストをトリミングする

label.text = [label.text stringByTrimmingCharactersInSet:
             [NSCharacterSet whitespaceAndNewlineCharacterSet]];

これは、sizeWithAttributesおよびにも適用される場合があります[label sizeToFit]

また、nsstringdrawingtextstorage message sent to deallocated instanceiOS 7.0デバイスを使用している場合はいつでもこれを処理します。


2

自動寸法の使用を改善する(Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

注意:1. UITableViewCellプロトタイプは適切に設計する必要があります(インスタンスの場合、忘れずにUILabel.numberOfLines = 0などを設定してください)2. HeightForRowAtIndexPathメソッドを削除します

ここに画像の説明を入力してください

ビデオ:https : //youtu.be/Sz3XfCsSb6k


ストーリーボードのプロトタイプセルで定義されている静的なセルの高さが必要です
Kirit Vaghela

これらの2行を追加し、UILabelが0行数に設定されていることを確認しました。宣伝どおりに機能しませんでした。この2行のコードだけで機能させるために、他に何をしましたか?
ウィル

1
また、セルの上部と下部にラベルの制約を固定する必要があります
Eugene Braginets

1
いいえ、これで十分です。スーパービューに固定されたラベルの自動レイアウト制約はありますか?
Eugene Braginets 2017



1

@Ayushの答えとして:

sizeWithFontApple Developerサイトで確認できるように、これは非推奨であるため、使用する必要がありますsizeWithAttributes

まあ、2019 + ではおそらくObjective-cとの代わりにSwiftを使用していると仮定するStringNSStringString事前定義されたフォントでaのサイズを取得する正しい方法は次のとおりです:

let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])

これを使用してフォントサイズをどのように決定できますか?
ジョセフアストラハン

@JosephAstrahanこれはフォントサイズを決定するためではなく、決定されたフォントで文字列のサイズ(CGSize)を取得するためのものです。ラベルのフォントサイズを取得する場合は、label.fontを簡単に使用できます
Romulo BM

あなたのアイデアを使用して、フォントサイズを多かれ少なかれ取得する方法を作成しました(stackoverflow.com/questions/61651614/…)。ラベルをクリックすると、正確なフォントは、私の投稿で確認できます。
ジョセフアストラハン

0
- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}

0

モノタッチが必要な場合のモノタッチは次のとおりです。

/// <summary>
/// Measures the height of the string for the given width.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="width">The width.</param>
/// <param name="padding">The padding.</param>
/// <returns></returns>
public static float MeasureStringHeightForWidth(this string text, UIFont font, float width, float padding = 20)
{
    NSAttributedString attributedString = new NSAttributedString(text, new UIStringAttributes() { Font = font });
    RectangleF rect = attributedString.GetBoundingRect(new SizeF(width, float.MaxValue), NSStringDrawingOptions.UsesLineFragmentOrigin, null);
    return rect.Height + padding;
}

これは次のように使用できます:

public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
    //Elements is a string array
    return Elements[indexPath.Row].MeasureStringHeightForWidth(UIFont.SystemFontOfSize(UIFont.LabelFontSize), tableView.Frame.Size.Width - 15 - 30 - 15);
}

0
CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;


-1

これはiOS 7では機能しませんでした。これをカスタムセルクラスに配置し、heightForCellAtIndexPathメソッドでメソッドを呼び出します。

アプリストアでアプリを表示すると、私のセルは説明セルに似ています。

最初にストーリーボードで、ラベルを 'attributedText'に設定し、行数を0に設定して(ラベルのサイズを自動的に変更します(iOS 6以降のみ))、ワードラップに設定します。

次に、カスタムセルクラスのセルのコンテンツのすべての高さを合計します。私の場合、常に「説明」(_descriptionHeadingLabel)と言うラベルが上部にあります。実際の説明(_descriptionLabel)を含むサイズが可変の小さいラベルは、セルの上部から見出し(_descriptionHeadingLabelTopConstraint)までの制約です。 。また、下部に少しスペースを空けるために3を追加しました(Appleが字幕タイプのセルに配置するのとほぼ同じ量です)。

- (CGFloat)calculateHeight
{
    CGFloat width = _descriptionLabel.frame.size.width;
    NSAttributedString *attributedText = _descriptionLabel.attributedText;
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];

    return rect.size.height + _descriptionHeadingLabel.frame.size.height + _descriptionHeadingLabelTopConstraint.constant + 3;
}

そして私のテーブルビューデリゲートでは:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    if (indexPath.row == 0) {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"descriptionCell"];
        DescriptionCell *descriptionCell = (DescriptionCell *)cell;
        NSString *text = [_event objectForKey:@"description"];
        descriptionCell.descriptionLabel.text = text;

        return [descriptionCell calculateHeight];
    }

    return 44.0f;
}

ifステートメントを少し「よりスマート」に変更して、実際にある種のデータソースからセル識別子を取得できます。私の場合、セルは特定の順序で固定されているため、ハードコードされます。


boundingRectWithSizeiOS 9.2の現在の問題は、iOS <9.2とは結果が異なります。あなたはこれを作るための他の最良の方法を見つけたか知っています。
jose920405 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.