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

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

177605 次浏览

改为使用sizeWithAttributes:,它现在接受NSDictionary。像这样传递带有UITextAttributeFont键和你的字体对象的pair:

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;

正如你在苹果开发者网站上看到的,sizeWithFont已弃用,所以我们需要使用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]}];
}

创建一个接受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;

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

如果你看一下新的NSString boundingRectWithSize:...函数,它会以与NSAttributeString相同的方式请求属性数组。如果我不得不猜测,iOS 7中这个新的NSString函数仅仅是iOS 6中NSAttributeString函数的包装。

在这一点上,如果你只支持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;

请注意文件中提到:

在ios7及更高版本中,此方法返回分数大小(在size . size . size中) 返回CGRect的组件);要使用返回的大小到大小 视图中,必须使用将其值提高到最近的更高整数

.使用ceil函数

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

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

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

#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]);
}

这样你只需要用sizeWithMyFont:找到/替换sizeWithFont:,你就可以开始了。

替代解决方案,

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];
}

你仍然可以使用sizeWithFont。但是,在iOS >= 7.0中,如果字符串包含开头和结尾空格或结束行\n,则会导致崩溃。

在使用文本之前修剪文本

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

这也适用于sizeWithAttributes[label sizeToFit]

而且,只要你在ios7.0设备中有nsstringdrawingtextstorage message sent to deallocated instance,它就会处理这个。

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

这些在ios 7中都不适用。这是我最后做的事情。我把它放在我的自定义单元格类中,并在highightforcellatindexpath方法中调用该方法。

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

首先在故事板中,将标签设置为'attributedText',将行数设置为0(这将自动调整标签的大小(仅限ios 6+)),并将其设置为换行。

然后我只是在我的自定义单元格类中添加单元格内容的所有高度。在我的例子中,我在顶部有一个标签,总是说“描述”(_descriptionHeadingLabel),一个较小的标签,大小可变,包含实际的描述(_descriptionLabel),从单元格的顶部到标题(_descriptionHeadingLabelTopConstraint)的约束。我还添加了3来留出底部的空间(大约与apple在subtitle type cell上的位置相同)。

- (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语句,让它更“聪明”一点,从某种数据源中获取单元格标识符。在我的例子中,单元格将被硬编码,因为它们将以特定的顺序固定数量。

在iOS7中,我需要逻辑来为tableview返回正确的高度:highightforrowatindexpath方法,但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
boundingRectWithSize:options:attributes:context:

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

        UIStringAttributes attributes = new UIStringAttributes
{
Font = UIFont.SystemFontOfSize(17)
};
var size = text.GetSizeUsingAttributes(attributes);

如果有人需要的话,这里有一个单一的对等物:

/// <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);
}

最好使用自动尺寸(Swift):

  tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension
< p >注: 1. UITableViewCell原型应该正确设计(对于实例不要忘记设置UILabel。numberolines = 0 etc) 2. 删除hightforrowatindexpath方法

enter image description here

< p >视频: https://youtu.be/Sz3XfCsSb6k < / p >
CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;

使用动态高度的多行标签可能需要额外的信息来正确设置大小。你可以使用sizeWithAttributes with 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 ' adjuststedsize '或CGRect作为矩形。size.height属性如果你在寻找高度。

更多关于NSParagraphStyle的信息:https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html

基于@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;
}

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

试试这个语法:

NSAttributedString *attributedText =
[[NSAttributedString alloc] initWithString:text
attributes:@{NSFontAttributeName: font}];
// 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));

正如@Ayush的回答:

正如你在苹果开发者网站上看到的,sizeWithFont已弃用,所以我们需要使用sizeWithAttributes

好吧,假设在2019年以后,你可能会使用斯威夫特String而不是Objective-c和NSString,下面是使用预定义字体获取String大小的正确方法:

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