我应该用什么替代已弃用的sizeWithFont:方法?

27

我有一个方法,可以根据字符串的长度(并使用相应的正确字体大小)为UITextView提供完美的尺寸:

- (NSInteger) heightOfLabel:(NSString*) string {
    CGSize maximumLabelSize = CGSizeMake([[UIScreen mainScreen] bounds].size.width - 40, FLT_MAX);
    CGSize expectedLabelSize = [[NSString stringTrimmedForLeadingAndTrailingWhiteSpacesFromString:string]
             sizeWithFont:[UIFont systemFontOfSize:15]
             constrainedToSize:maximumLabelSize 
             lineBreakMode:NSLineBreakByWordWrapping];

    return expectedLabelSize.height + 5;
}

事实上,即使在iOS7中,它仍然完美地适合我。尽管现在它会出现一个警告方法,说我不应该使用'sizeWithFont:contrainedToSize:lineBreakMode'。

它现在说我应该使用-boundingRectWithSize:options:attributes:context:

这种方法对于iOS7并不新,因此我认为可以在Stack Overflow上提问,而无需跨越到官方的苹果开发者论坛。

我有三个问题:

1)因为它已被弃用,是否意味着我应该替换它,尽管它仍然正常工作?

2)我尝试了许多不同的boundingRectWithSize:方法,使用各种变量,但它从来都不是完美的,它总是略微偏差(如许多stackoverflow问题所指出的那样) - 是否有一种完美的替代方法,使用这个未弃用的方法与我的先前方法完全相同,且尽可能少的麻烦?

3)为什么要移除这种方法?是因为与另一种方法重叠吗?


请参考此链接:http://www.cnblogs.com/lisa090818/p/3445161.html。 - yazh
4个回答

59

在试错了一个小时之后,我设法让它正常工作:

CGSize maximumLabelSize = CGSizeMake(tableView.width, MAXFLOAT);

NSStringDrawingOptions options = NSStringDrawingTruncatesLastVisibleLine |
                                 NSStringDrawingUsesLineFragmentOrigin;

NSDictionary *attr = @{NSFontAttributeName: [UIFont systemFontOfSize:15]};
CGRect labelBounds = [string boundingRectWithSize:maximumLabelSize 
                                          options:options
                                       attributes:attr
                                          context:nil];

更新:

正如T先生在下面的答案中提到的那样:在iOS 7及更高版本中,此方法返回分数大小(在返回的CGRect的大小组件中);要使用返回的大小调整视图大小,您必须使用ceil函数将其值提高到最近的较高整数。建议使用ceilf函数。

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

1
谢谢您。我现在正在使用类似的东西来为iOS6创建NSAttributedString,它使用您创建的stringAttributes字典并在属性字符串上调用boundingRectWithSize:options:context:。 - AidenMontgomery
谢谢!非常有用! - Andres

14

我认为这个函数被弃用,是因为NSString+UIKit函数系列基于的UIStringDrawing库是不安全的线程。如果你尝试在非主线程上运行它们(就像任何其他UIKit功能一样),你将得到不可预测的结果。特别是,如果你在多个线程上同时运行函数,它可能会导致应用程序崩溃。这就是为什么在iOS 6中,他们为NSAttributedStrings引入了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:CGSizeMake(width, CGFLOAT_MAX)];

可以被替换为:

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

请注意,文档中提到:

在 iOS 7 及更高版本中,此方法返回分数大小(在返回的 CGRect 的 size 组件中);为了使用返回的大小来调整视图大小,您必须使用 ceil 函数将其值提高到最近的较高整数。

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


```objective-c CGSize size = // calculated size CGFloat width = ceil(size.width); CGFloat height = ceil(size.height); ```
CGFloat height = ceilf(size.height);
CGFloat width  = ceilf(size.width);

4
不解决换行模式问题。 - Firdous

6

对于换行问题:

- (CGFloat)heightNeededForText:(NSString *)text withFont:(UIFont *)font width:(CGFloat)width lineBreakMode:(NSLineBreakMode)lineBreakMode {
    NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        paragraphStyle.lineBreakMode = lineBreakMode;
    CGSize size = [text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
                                     options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                  attributes:@{ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle }
                                     context:nil].size;

    return ceilf(size.height);
}

3

挪威亚历山大的回答的Swift版本...

func heightNeededForText(text: NSString, withFont font: UIFont, width: CGFloat, lineBreakMode:NSLineBreakMode) -> CGFloat {
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineBreakMode = lineBreakMode
        let size: CGSize = text.boundingRectWithSize(CGSizeMake(width, CGFloat.max), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: [ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle], context: nil).size//text boundingRectWithSize:CGSizeMake(width, CGFLOAT_MA

        return ceil(size.height);
    }

在你想获取高度的代码中,只需像下面这样调用方法...
let size = self.heightNeededForText(text as NSString, withFont: UIFont.systemFontOfSize(15.0), width: scrollView.frame.size.width - 20, lineBreakMode: NSLineBreakMode.ByWordWrapping) //Can edit the font size and LinebreakMode

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