“sizeWithFont:constrainedToSize:lineBreakMode:”已被弃用:

29

在Xcode 5上将一个项目从iOS5.0升级到iOS7 / iOS6。下面的代码会产生编译时警告:

'sizeWithFont:constrainedToSize:lineBreakMode:'已弃用:自iOS 7.0起首次弃用-使用-boundingRectWithSize:options:attribiutes:context

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0)
    {
        self.lblHidden.frame = CGRectMake(58, 228, 945, 9999);
        self.lblHidden.text = detailShareObj.pDesc;
        CGSize size = [detailShareObj.pDesc sizeWithFont:self.lblHidden.font constrainedToSize:self.lblHidden.frame.size lineBreakMode:NSLineBreakByWordWrapping];
        return 228.0+size.height+20;

    }
    else if (indexPath.section == 1)
    {
        NSString *tempPointStr = (self.shortDescArray)[indexPath.row];

        self.lblHidden.frame = CGRectMake(58, 0, 945, 9999);
        self.lblHidden.text = tempPointStr;
        CGSize size = [tempPointStr sizeWithFont:self.lblHidden.font
                               constrainedToSize:self.lblHidden.frame.size
                                   lineBreakMode:NSLineBreakByWordWrapping];

            return 50.0f;
    }

我尝试了其他地方提供的建议,但如果有人能帮忙提供代码所需的更正,将不胜感激。

6个回答

64
我不建议仅仅掩盖已过时的函数警告。他们之所以将其弃用是有原因的。我认为这个函数被弃用是因为NSString+UIKit系列函数基于UIStringDrawing库,而该库不支持多线程。如果您尝试在非主线程上运行它们(就像其他任何UIKit功能一样),则会出现不可预测的行为。特别地,如果您同时在多个线程上运行该函数,则可能会导致应用程序崩溃。这就是为什么在iOS 6中,他们引入了一个boundingRectWithSize:...方法用于NSAttributedStrings。这是在NSStringDrawing库的基础上构建的,并且是线程安全的。
如果您查看新的NSString boundingRectWithSize:...函数,它会以与NSAttributeString相同的方式请求属性数组。如果我猜测的话,iOS 7中的这个新的NSString函数只是iOS 6中NSAttributeString函数的包装器。
在这个问题上,如果您只支持iOS 6和iOS 7,则我肯定会将所有NSString的sizeWithFont:...更改为NSAttributeString的boundingRectWithSize。如果您恰好遇到奇怪的多线程情况,这将为您节省很多麻烦!以下是我如何将NSString的sizeWithFont:constrainedToSize:转换为NSAttributeString的boundingRectWithSize:
原来的代码是:
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);

是的!我也认为掩盖问题并不是一个解决方案。让我尝试实施您的建议,如果可能的话,我会发布修改后的代码。 - iSrini
11
使用 CGRectIntegral(rect) 要比分别对高度和宽度进行向上取整更加简洁。这样可以使得尺寸更加整齐。 - memmons
一定要记得实现四舍五入函数,因为在没有实现之前,我得到的标签会被略微截断! - Evan R
新的 boundingRectWithSize 在使用 NSStringDrawingUsesLineFragmentOrigin 时总是使用换行规则 NSLineBreakByClipping,因此当我们使用其他换行规则时,它会返回不正确的大小。例如:NSLineBreakByCharWrapping - jeeeyul

44

如果你希望其兼容iOS7及以下版本,尝试使用此版本(启用ARC):

CGSize size;

if ([tempPointStr respondsToSelector:
     @selector(boundingRectWithSize:options:attributes:context:)])
{
  NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
  paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
  paragraphStyle.alignment = NSTextAlignmentLeft;

  NSDictionary * attributes = @{NSFontAttributeName : self.lblHidden.font,
                      NSParagraphStyleAttributeName : paragraphStyle};

  size = [tempPointStr boundingRectWithSize:self.lblHidden.frame.size
                                    options:NSStringDrawingUsesFontLeading
                                           |NSStringDrawingUsesLineFragmentOrigin
                                 attributes:attributes
                                    context:nil].size;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
  size = [tempPointStr sizeWithFont:self.lblHidden.font
                  constrainedToSize:self.lblHidden.frame.size
                      lineBreakMode:NSLineBreakByWordWrapping];
#pragma clang diagnostic pop
}

注意:这只是一个else-if情况的例子,你可能需要根据你想要的进行一些修改。 ;)


1
所以,对于iOS7,您使用CharWrapping,而对于iOS6,您使用WordWrapping?;) - Cœur

10

iOS7版本,请替换:

CGSize size = [tempPointStr sizeWithFont:self.lblHidden.font
                       constrainedToSize:self.lblHidden.frame.size
                           lineBreakMode:NSLineBreakByWordWrapping];

使用:

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; //set the line break mode
NSDictionary *attrDict = [NSDictionary dictionaryWithObjectsAndKeys:self.lblHidden.font, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
CGSize size = [tempPointStr boundingRectWithSize:self.lblHidden.frame.size
                                         options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin
                                      attributes:attrDict context:nil].size;

2

您可以使用:

UIFont *font = [UIFont boldSystemFontOfSize:16];

CGRect new = [string boundingRectWithSize:CGSizeMake(200, 300)
    options:NSStringDrawingUsesFontLeading 
    attributes:@{NSFontAttributeName: font} 
    context:nil];

CGSize stringSize= new.size;

1
问题描述: boundingRectWithSize:options:attributes:context 方法存在问题,如果字符串中包含"\n"(换行符),则无法正确计算高度。因此,该代码针对给定宽度(inWidth)分别计算每行的大小:

NSArray *brokenByLines=[string componentsSeparatedByString:@"\n"];
CGFloat height=0.0;
CGFloat maxWidth=0.0;
for (NSString* actString in brokenByLines) {
    CGRect tSize=[actString boundingRectWithSize:CGSizeMake(inWidth, 600) options:(NSStringDrawingUsesLineFragmentOrigin | NSLineBreakByWordWrapping) attributes:@{NSFontAttributeName: inFont} context:nil];
    if (maxWidth<tSize.size.width) {
        maxWidth=tSize.size.width;
    }
    height+=tSize.size.height;
}
CGSize size= CGSizeMake(ceil(maxWidth), ceil(height));

如果你有一个双重换行符(例如“\n\n”),那么你会遇到麻烦,因为 boundingRectWithSize 函数无法正确地调整空字符串的大小。我添加了 if ([actString isEqual: @""]) {height += fontSize} else{ <your current loop code>}。请注意,这需要你同时传递字体大小。 - BFar
我无法编辑之前的评论,但我应该提到对于空字符串,我将fontSize乘以1.125。我观察到boundingRectWithSize这样计算每行高度。 - BFar

1
如果你的目标是 iOS 6.0+,你仍然可以使用 sizeWithFont:constrainedToSize:lineBreakMode:。只需确保项目的 iOS Deployment Target 设置为 6.0,编译器就不会给出这些警告。
(你可以通过在“信息”部分中点击蓝色项目选项卡(通常位于左侧项目导航窗格的顶部)来找到它。)
如果你只针对 iOS 7.0+,你应该使用新的方法 boundingRectWithSize:options:attributes:context
你可以 在这里找到苹果文档上关于这个新方法的内容。

是的,当我们为iOS 7进行操作时才会出现这个问题,而且你的建议已经被编译器本身建议了。我需要帮助来实际使用boundingRectWithSize:options:attributes:context!无论如何,感谢你的建议。 - iSrini

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接