我应该用什么来替换不赞成的sizeWithFont:方法?

时间:2021-05-24 07:12:40

I have a method that gives me the perfect size for a UITextView given a length of string (with the corresponding correct font size) :

我有一个方法,在给定字符串长度(对应的字体大小)的情况下,为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;
}

In fact, it still gives me a perfect fit, even in iOS7. Although now it comes up with a warning method that says I shouldn't use 'sizeWithFont:contrainedToSize:lineBreakMode'.

事实上,即使在iOS7系统中,它也能让我完美的搭配。虽然现在它提出了一个警告方法,说我不应该使用“sizeWithFont:contrainedToSize:lineBreakMode”。

It now says I should be using -boundingRectWithSize:options:attributes:context:

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

This method isn't new to iOS7 and therefore i figure that it is okay to ask it on stack overflow, rather than going across to the official apple developers forum.

这个方法对iOS7来说并不新鲜,因此我认为在stack overflow上询问它是可以的,而不是去到官方的苹果开发者论坛。

I have three questions:

我有三个问题:

1) Because it is deprecated, does that mean I should definitely replace it, despite it still working?

1)因为它被弃用了,这是否意味着我一定要替换它,尽管它还在工作?

2) I have tried many different boundingRectWithSize: methods, with various variables but it is never perfect, it always seems to be slightly out (as many * questions point out) - is there a perfect replacement with this none-deprecated method that does exactly the same as my previous method with as minimal hassle?

2)我已经尝试过许多不同的boundingRectWithSize:方法、各种变量,但它从来都不是完美的,它似乎总是略(正如许多*问题指出)——有一个完美的替代这个none-deprecated方法做一模一样我以前的方法,如最小的麻烦吗?

3) why remove this method? Is it because of the overlap with this other method?

3)为什么要删除这个方法?是因为和其他方法重叠吗?

4 个解决方案

#1


57  

After an hour of trial error I managed to make it work:

经过一个小时的尝试,我终于成功了:

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

Update:

更新:

As Mr. T mentions in answer below : In iOS 7 and later, this method returns fractional sizes (in the size component of the returned CGRect); to use a returned size to size views, you must use raise its value to the nearest higher integer using the ceil function. ceilf function is recommended to use.

正如T先生在下面的回答中提到的:在ios7和以后的版本中,这个方法返回小数位数(在返回的CGRect的size组件中);要使用返回的大小到大小视图,必须使用使用ceil函数将其值提高到最近的更高的整数。建议使用f函数。

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

#2


13  

I believe the function was deprecated because that series of NSString+UIKit functions were based on the UIStringDrawing library, which wasn't thread safe. If you tried to run them not on the main thread (like any other UIKit functionality), you'll get unpredictable behaviors. In particular, if you ran the function on multiple threads simultaneously, it'll probably crash your app. This is why in iOS 6, they introduced a the boundingRectWithSize:... method for NSAttributedStrings. This was built on top of the NSStringDrawing libraries and is thread safe.

我认为这个函数被废弃了,因为一系列NSString+UIKit函数基于uistringdraw库,它不是线程安全的。如果您试图在主线程上运行它们(与任何其他UIKit功能一样),您将得到不可预测的行为。特别是,如果你同时在多个线程上运行这个函数,它可能会让你的应用程序崩溃。nsattributedstring的方法。它构建在nsstringdraw库之上,是线程安全的。

If you look at the new NSString boundingRectWithSize:... function, it asks for an attributes array in the same manner as a NSAttributeString. If I had to guess, this new NSString function in iOS 7 is merely a wrapper for the NSAttributeString function from iOS 6.

如果您查看新的NSString boundingRectWithSize:…函数的作用是:请求一个与NSAttributeString相同的属性数组。如果我不得不猜测,ios7中新的NSString函数仅仅是ios6中NSAttributeString函数的包装。

On that note, if you were only supporting iOS 6 and iOS 7, then I would definitely change all of your NSString's sizeWithFont:... to the NSAttributeString's boundingRectWithSize. It'll save you a lot of headache if you happen to have a weird multi-threading corner case! Here's how I converted NSString's sizeWithFont:constrainedToSize::

注意,如果您只支持ios6和ios7,那么我肯定会更改所有NSString的sizeWithFont:……NSAttributeString boundingRectWithSize的。如果您碰巧有一个奇怪的多线程的死角,那么它将为您省去很多麻烦!以下是我如何转换NSString的sizeWithFont:constrainedToSize:::

What used to be:

曾经是什么:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font 
               constrainedToSize:CGSizeMake(width, CGFLOAT_MAX)];

Can be replaced with:

可以替换为:

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;

Please note the documentation mentions:

请注意所提到的文件:

In iOS 7 and later, this method returns fractional sizes (in the size component of the returned CGRect); to use a returned size to size views, you must use raise its value to the nearest higher integer using the ceil function.

在ios7和以后的版本中,该方法返回小数大小(在返回的CGRect的size组件中);要使用返回的大小到大小视图,必须使用使用ceil函数将其值提高到最近的更高的整数。

So to pull out the calculated height or width to be used for sizing views, I would use:

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

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

#3


5  

For linebreak issue:

linebreak问题:

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

#4


2  

Swift version of the Alexander of Norway's answer...

挪威亚历山大的迅速版本……

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

In the code where you want to get the height just call the method like below...

在你想要得到高度的代码中,只需调用下面的方法…

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

#1


57  

After an hour of trial error I managed to make it work:

经过一个小时的尝试,我终于成功了:

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

Update:

更新:

As Mr. T mentions in answer below : In iOS 7 and later, this method returns fractional sizes (in the size component of the returned CGRect); to use a returned size to size views, you must use raise its value to the nearest higher integer using the ceil function. ceilf function is recommended to use.

正如T先生在下面的回答中提到的:在ios7和以后的版本中,这个方法返回小数位数(在返回的CGRect的size组件中);要使用返回的大小到大小视图,必须使用使用ceil函数将其值提高到最近的更高的整数。建议使用f函数。

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

#2


13  

I believe the function was deprecated because that series of NSString+UIKit functions were based on the UIStringDrawing library, which wasn't thread safe. If you tried to run them not on the main thread (like any other UIKit functionality), you'll get unpredictable behaviors. In particular, if you ran the function on multiple threads simultaneously, it'll probably crash your app. This is why in iOS 6, they introduced a the boundingRectWithSize:... method for NSAttributedStrings. This was built on top of the NSStringDrawing libraries and is thread safe.

我认为这个函数被废弃了,因为一系列NSString+UIKit函数基于uistringdraw库,它不是线程安全的。如果您试图在主线程上运行它们(与任何其他UIKit功能一样),您将得到不可预测的行为。特别是,如果你同时在多个线程上运行这个函数,它可能会让你的应用程序崩溃。nsattributedstring的方法。它构建在nsstringdraw库之上,是线程安全的。

If you look at the new NSString boundingRectWithSize:... function, it asks for an attributes array in the same manner as a NSAttributeString. If I had to guess, this new NSString function in iOS 7 is merely a wrapper for the NSAttributeString function from iOS 6.

如果您查看新的NSString boundingRectWithSize:…函数的作用是:请求一个与NSAttributeString相同的属性数组。如果我不得不猜测,ios7中新的NSString函数仅仅是ios6中NSAttributeString函数的包装。

On that note, if you were only supporting iOS 6 and iOS 7, then I would definitely change all of your NSString's sizeWithFont:... to the NSAttributeString's boundingRectWithSize. It'll save you a lot of headache if you happen to have a weird multi-threading corner case! Here's how I converted NSString's sizeWithFont:constrainedToSize::

注意,如果您只支持ios6和ios7,那么我肯定会更改所有NSString的sizeWithFont:……NSAttributeString boundingRectWithSize的。如果您碰巧有一个奇怪的多线程的死角,那么它将为您省去很多麻烦!以下是我如何转换NSString的sizeWithFont:constrainedToSize:::

What used to be:

曾经是什么:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font 
               constrainedToSize:CGSizeMake(width, CGFLOAT_MAX)];

Can be replaced with:

可以替换为:

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;

Please note the documentation mentions:

请注意所提到的文件:

In iOS 7 and later, this method returns fractional sizes (in the size component of the returned CGRect); to use a returned size to size views, you must use raise its value to the nearest higher integer using the ceil function.

在ios7和以后的版本中,该方法返回小数大小(在返回的CGRect的size组件中);要使用返回的大小到大小视图,必须使用使用ceil函数将其值提高到最近的更高的整数。

So to pull out the calculated height or width to be used for sizing views, I would use:

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

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

#3


5  

For linebreak issue:

linebreak问题:

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

#4


2  

Swift version of the Alexander of Norway's answer...

挪威亚历山大的迅速版本……

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

In the code where you want to get the height just call the method like below...

在你想要得到高度的代码中,只需调用下面的方法…

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