With what should I replace the deprecated sizeWithFont: method?
Solution 1:
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.
CGFloat height = ceilf(labelBounds.size.height);
Solution 2:
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.
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.
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:
:
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.
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);
Solution 3:
For linebreak issue:
- (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);
}
Solution 4:
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