326

在 iOS 7 中,sizeWithFont:现已弃用。我现在如何将 UIFont 对象传递给替换方法sizeWithAttributes:

4

20 回答 20

527

改为使用sizeWithAttributes:,现在需要一个NSDictionary. 传入带有 key 的对UITextAttributeFont和你的字体对象,如下所示:

CGRect rawRect = {};
rawRect.size = [string sizeWithAttributes: @{
    NSFontAttributeName: [UIFont systemFontOfSize:17.0f],
}];

// Values are fractional -- you should take the ceil to get equivalent values
CGSize adjustedSize = CGRectIntegral(rawRect).size;
于 2013-09-19T14:47:10.620 回答
173

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

如果您查看新NSString boundingRectWithSize:...函数,它会以与 a 相同的方式请求属性数组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 及更高版本中,此方法返回小数大小(在返回的大小组件中CGRect);要使用返回的大小来调整视图大小,您必须使用 ceil 函数将其值提高到最接近的更高整数。

因此,要提取用于调整视图大小的计算高度或宽度,我会使用:

CGFloat height = ceilf(size.height);
CGFloat width  = ceilf(size.width);
于 2013-09-23T03:30:16.913 回答
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]}];
}
于 2013-09-19T15:27:15.110 回答
17

我创建了一个类别来处理这个问题,这里是:

#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:可以了。

于 2013-09-25T12:45:30.150 回答
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
于 2013-12-26T02:13:52.263 回答
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

于 2015-08-12T21:13:04.550 回答
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));
于 2016-06-28T11:52:09.243 回答
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];
}
于 2013-12-01T11:02:59.043 回答
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;
于 2013-09-20T07:29:07.817 回答
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;
}

我只是使用结果帧的大小。

于 2015-10-05T21:45:19.210 回答
2

Better use automatic dimensions (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

NB: 1. UITableViewCell prototype should be properly designed (for the instance don't forget set UILabel.numberOfLines = 0 etc) 2. Remove HeightForRowAtIndexPath method

enter image description here

VIDEO: https://youtu.be/Sz3XfCsSb6k

于 2015-04-09T13:25:00.910 回答
2

您仍然可以使用sizeWithFont. 但是,在 iOS >= 7.0 方法中,如果字符串包含前导和尾随空格或结束行,则会导致崩溃\n

在使用之前修剪文本

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

这也可能适用于sizeWithAttributesand [label sizeToFit]

此外,只要您使用nsstringdrawingtextstorage message sent to deallocated instanceiOS 7.0 设备,它就会处理这个问题。

于 2013-12-10T09:55:14.697 回答
1
boundingRectWithSize:options:attributes:context:
于 2014-04-02T02:52:37.967 回答
1

Xamarin 中接受的答案是(使用 sizeWithAttributes 和 UITextAttributeFont):

        UIStringAttributes attributes = new UIStringAttributes
        { 
            Font = UIFont.SystemFontOfSize(17) 
        }; 
        var size = text.GetSizeUsingAttributes(attributes);
于 2014-06-25T04:13:25.960 回答
1

正如@Ayush 的回答:

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

好吧,假设在 2019+ 年您可能使用的是SwiftString不是 Objective-c 和,这是使用预定义字体NSString获取 a 大小的正确方法:String

let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])
于 2019-04-22T20:07:41.303 回答
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);
}
于 2014-11-12T12:44:54.240 回答
0
- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}
于 2013-12-10T11:06:17.090 回答
0
CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;
于 2015-07-03T06:41:20.170 回答
0

试试这个语法:

NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];
于 2016-06-07T05:51:02.410 回答
-1

在 ios 7 中,这些都不适合我。这就是我最终要做的。我把它放在我的自定义单元类中,并在我的 heightForCellAtIndexPath 方法中调用该方法。

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

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

然后我只需将自定义单元格类中单元格内容的所有高度相加。在我的例子中,我在顶部有一个标签,总是说“描述”(_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 语句更改为“更智能”一点,并从某种数据源中实际获取单元格标识符。在我的情况下,单元格将被硬编码,因为它们将按特定顺序固定数量。

于 2013-12-20T05:40:33.220 回答