在iOS 7中替换已弃用的sizeWithFont:吗?


Answers:


521

使用sizeWithAttributes:代替,现在需要一个NSDictionaryUITextAttributeFont像这样传递带有key 和您的字体对象的对:

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
当使用NSStringand和a UILabel(并非总是如此,但经常如此)时,为了防止重复的代码/等,您还可以替换[UIFont systemFontOfSize:17.0f]label.font-通过引用现有数据而不是多次键入或遍历常量来帮助代码维护。地点等
toblerpwn13年

8
@toblerpwn,标签可能不存在,而您正在尝试计算理论标签。
botbot

5
我将如何使用此示例获取带有固定宽度(例如250)的标签的size.height?或者,如果带有autolayyout的女巫的标签占用了一定比例的宽度,然后我进入了Landscapemode。
Pedroinpeace 2013年

12
@Pedroinpeace 在大多数情况下boundingRectWithSize:options:attributes:context:,您可以使用传递CGSizeMake(250.0f, CGFLOAT_MAX)
James Kuang

9
注意sizeWithAttributes:的其他事项不是100%等效的。sizeWithFont用于将大小四舍五入为整数(像素)值。建议将ceilf用于合成的高度/宽度,否则,如果将其用于位置计算,则可能会产生模糊的伪像(特别是非视网膜HW)。
尼克H247 2014年

172

我认为该函数已被弃用,因为该系列NSString+UIKit函数(sizewithFont:...,等)基于UIStringDrawing库,这不是线程安全的。如果您尝试不在主线程上运行它们(像其他任何UIKit功能一样),则会出现无法预测的行为。特别是,如果您同时在多个线程上运行该函数,则可能会使您的应用程序崩溃。这就是为什么在iOS 6中,他们引入了的boundingRectWithSize:...方法NSAttributedString。它建立在NSStringDrawing库的顶部,并且是线程安全的。

如果您查看新NSString boundingRectWithSize:...函数,它将以与相同的方式请求属性数组NSAttributeString。如果我不得不猜测,NSStringiOS 7中的这个新功能仅仅是NSAttributeStringiOS 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及更高版本中,此方法返回小数大小(以return的大小组成CGRect);要使用返回的大小调整视图大小,必须使用ceil函数将其值提高到最接近的较大整数。

因此,要拉出计算出的高度或宽度以用于调整视图大小,我将使用:

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

在iOS 6.0中,不推荐使用boundingRectWithSize。
尼拉夫

2
@Nirav我看不到任何有关弃用的内容。您能否指出它已过时的地方?谢谢!(developer.apple.com/library/ios/documentation/uikit/reference/...
T先生

2
也许@Nirav表示它不适用于iOS 6中的NSString(答案中间接提到)?
newenglander

1
@VagueExplanation我刚刚尝试过,并且NSAttributedString仍然不建议使用boundingRectForSize。它也没有说文档中不推荐使用。
T先生

5
很棒使用ceilf()提及。没有其他地方提到过,我的身材总是太小。谢谢!
乔纳森·布朗

29

正如您sizeWithFont在Apple 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 2013年

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
虽然这会在ios7 +上生成一个编译器警告以引用sizeWithFont,或者在<ios7上生成一个编译警告/错误以引用sizeWithFont!最好使用宏而不是检查responsesToSelector-如有必要。但是,由于您无法再使用ioS6 SDK交付给苹果,因此可能不是!
尼克H247 2014年

将其添加到禁止警告中#pragma GCC诊断已忽略“ -Wdeprecated-declarations”
Ryan Heitner

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

使用动态高度的多行标签可能需要其他信息才能正确设置尺寸。您可以将sizeWithAttributes与UIFont和NSParagraphStyle一起使用,以指定字体和换行模式。

您将定义段落样式并使用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之后立即存在语法问题。
克里斯·普林斯

感谢您捕获该语法问题,克里斯
比特和

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

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上均产生编译器警告。并且每种行为都有很大不同!
尼克H247 2014年

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 instance在iOS 7.0设备中都可以处理此问题。


2

更好地使用自动尺寸(快速):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

注意:1.应正确设计UITableViewCell原型(为此实例,请不要忘记设置UILabel.numberOfLines = 0等)2.删除HeightForRowAtIndexPath方法

在此处输入图片说明

视频:https//youtu.be/Sz3XfCsSb6k


情节
提要

添加了这两行,并确保我的UILabel设置为0行数。不能像宣传的那样工作。您还做了什么使它仅适用于这两行代码?
威尔

1
嗯,您还必须将标签约束固定在单元格的顶部和底部
Eugene Braginets

1
不,这应该足够了。您对固定到超级视图的标签有自动布局约束吗?
Eugene Braginets '17



1

作为@Ayush的答案:

正如您sizeWithFont在Apple Developer网站上看到的那样,它已被弃用,因此我们需要使用sizeWithAttributes

好吧,假设在2019年以后,您可能正在使用SwiftString不是Objective-c和NSString,这是String使用预定义字体获取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;

0

试试这个语法:

NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];

-1

在ios 7中,这些都不对我有用。这就是我最终要做的。我将其放在自定义单元格类中,然后在heightForCellAtIndexPath方法中调用该方法。

在应用商店中查看应用时,我的单元格与描述单元格相似。

首先在情节提要中,将标签设置为“ attributedText”,将行数设置为0(这将自动调整标签的大小(仅适用于iOS 6+))并将其设置为自动换行。

然后,我将自定义单元格类中单元格内容的所有高度加起来。在我的情况下,我在顶部始终带有一个“ Description”(_ descriptionHeadingLabel)标签,这是一个较小的标签,其大小可变,其中包含实际描述(_descriptionLabel),这是从单元格顶部到标题(_descriptionHeadingLabelTopConstraint)的约束。我还添加了3个字符,以使底部间隔开一些(苹果在字幕类型单元格上放置的空间大致相同)。

- (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语句更改为“更智能”,并实际上从某种数据源获取单元格标识符。在我的情况下,单元将被硬编码,因为将有固定数量的特定顺序的单元。


boundingRectWithSize在ios 9.2中存在问题,结果与ios <9.2不同。您发现或知道其他最佳方法可以做到这一点。
jose920405 '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.