Replacement for deprecated sizeWithFont: in iOS 7? - objective-c

In iOS 7, sizeWithFont: is now deprecated. How do I now pass in the UIFont object into the replacement method sizeWithAttributes:?

Use sizeWithAttributes: instead, which now takes an NSDictionary. Pass in the pair with key UITextAttributeFont and your font object like this:
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;

I believe the function was deprecated because that series of NSString+UIKit functions (sizewithFont:..., etc) 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 NSAttributedString. 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 sizeWithFont:... to the NSAttributeString 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 sizeWithFont:constrainedToSize::
What used to be:
NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font
constrainedToSize:(CGSize){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:(CGSize){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);

As you can see sizeWithFont at Apple Developer site it is deprecated so we need to use 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]}];
}

I created a category to handle this problem, here it is :
#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]);
}
This way you only have to find/replace sizeWithFont: with sizeWithMyFont: and you're good to go.

In iOS7 I needed the logic to return the correct height for the tableview:heightForRowAtIndexPath method, but the sizeWithAttributes always returns the same height regardless of the string length because it doesn't know that it is going to be put in a fixed width table cell. I found this works great for me and calculates the correct height taking in consideration the width for the table cell! This is based on Mr. T's answer above.
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

Multi-line labels using dynamic height may require additional information to set the size properly. You can use sizeWithAttributes with UIFont and NSParagraphStyle to specify both the font and the line-break mode.
You would define the Paragraph Style and use an NSDictionary like this:
// 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];
You can use the CGSize 'adjustedSize' or CGRect as rect.size.height property if you're looking for the height.
More info on NSParagraphStyle here: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html

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

Create a function that takes a UILabel instance. and returns 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;

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

Building on #bitsand, this is a new method I just added to my NSString+Extras category:
- (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;
}
I just use the size of the resulting frame.

You can still use sizeWithFont. but, in iOS >= 7.0 method cause crashing if the string contains leading and trailing spaces or end lines \n.
Trimming text before using it
label.text = [label.text stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
That's also may apply to sizeWithAttributes and [label sizeToFit].
also, whenever you have nsstringdrawingtextstorage message sent to deallocated instance in iOS 7.0 device it deals with this.

Better use automatic dimensions (Swift):
tableView.estimatedRowHeight = 68.0
tableView.rowHeight = UITableViewAutomaticDimension
NB:
1. UITableViewCell prototype should be properly designed (for the instance don't forget set UILabel.numberOfLines = 0 etc)
2. Remove HeightForRowAtIndexPath method
VIDEO:
https://youtu.be/Sz3XfCsSb6k

boundingRectWithSize:options:attributes:context:

Accepted answer in Xamarin would be (use sizeWithAttributes and UITextAttributeFont):
UIStringAttributes attributes = new UIStringAttributes
{
Font = UIFont.SystemFontOfSize(17)
};
var size = text.GetSizeUsingAttributes(attributes);

As the #Ayush answer:
As you can see sizeWithFont at Apple Developer site it is deprecated so we need to use sizeWithAttributes.
Well, supposing that in 2019+ you are probably using Swift and String instead of Objective-c and NSString, here's the correct way do get the size of a String with predefined font:
let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])

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

Here is the monotouch equivalent if anyone needs it:
/// <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;
}
which can be used like this:
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);
}

CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;

Try this syntax:
NSAttributedString *attributedText =
[[NSAttributedString alloc] initWithString:text
attributes:#{NSFontAttributeName: font}];

None of this worked for me in ios 7. Here is what I ended up doing. I put this in my custom cell class and call the method in my heightForCellAtIndexPath method.
My cell looks similar to the description cell when viewing an app in the app store.
First in the storyboard, set your label to 'attributedText', set the number of lines to 0 (which will resize the label automatically (ios 6+ only)) and set it to word wrap.
Then i just add up all the heights of the content of the cell in my custom Cell Class. In my case I have a Label at the top that always says "Description" (_descriptionHeadingLabel), a smaller label that is variable in size that contains the actual description (_descriptionLabel) a constraint from the top of the cell to the heading (_descriptionHeadingLabelTopConstraint). I also added 3 to space out the bottom a little bit (about the same amount apple places on the 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;
}
And in my Table View delegate:
- (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;
}
You can change the if statement to be a little 'smarter' and actually get the cell identifier from some sort of data source. In my case the cells are going to be hard coded since there will be fixed amount of them in a specific order.

Related

Autosize Text in Label for PaintCode CGContext

I'm using the following to draw text inside a Bezier Path. How can i adjust this to allow the text to autosize.
EDIT
I was able to update to iOS7 methods but still nothing. I can autosize text within a UILabel fine, but because this is CGContext it is harder
NSString* textContent = #"LOCATION";
NSMutableParagraphStyle* locationStyle = NSMutableParagraphStyle.defaultParagraphStyle.mutableCopy;
locationStyle.alignment = NSTextAlignmentCenter;
NSDictionary* locationFontAttributes = #{NSFontAttributeName: [UIFont fontWithName:myFont size: 19], NSForegroundColorAttributeName: locationColor, NSParagraphStyleAttributeName: locationStyle};
CGFloat locationTextHeight = [textContent boundingRectWithSize: CGSizeMake(locationRect.size.width, INFINITY) options: NSStringDrawingUsesLineFragmentOrigin attributes: locationFontAttributes context: nil].size.height;
CGContextSaveGState(context);
CGContextClipToRect(context, locationRect);
[textContent drawInRect: CGRectMake(CGRectGetMinX(locationRect), CGRectGetMinY(locationRect) + (CGRectGetHeight(locationRect) - locationTextHeight) / 2, CGRectGetWidth(locationRect), locationTextHeight) withAttributes: locationFontAttributes];
CGContextRestoreGState(context);
Try using this method of NSAttributedString:
- (CGRect)boundingRectWithSize:(CGSize)size
options:(NSStringDrawingOptions)options
context:(NSStringDrawingContext *)context;
Where the context will provide you actualScaleFactor.
The usage is something like this:
NSAttributedString *string = ...;
NSStringDrawingContext *context = [NSStringDrawingContext new];
context.minimumScaleFactor = 0.5; // Set your minimum value.
CGRect bounds = [string boundingRectWithSize:maxSize
options:NSStringDrawingUsesLineFragmentOrigin
context:context];
CGFloat scale = context. actualScaleFactor;
// Use this scale to multiply font sizes in the string, so it will fit.

Finding the width of an NSString as a CGFloat

I know that this has been posted already, but the solution i found
-(CGFloat)widthOfText{
CGRect idealFrame = [self.text boundingRectWithSize:self.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName:self.font }
context:nil];
return idealFrame.size.width;
}
self is a UILabel
isn't working.
I'm trying to place a button at the end of a string so I need the width of the string to calculate the buttons absolute placement
can anybody explain why this isn't working or suggest a different method?
One or more of the inputs is not what you expect, NSLog() self.text, self.frame, self.font -- basic debugging.
It is really better to pass in parameters than use ivars, this allows easier testing. It also removes the dependency on the class.
Example that works properly:
NSString *text = #"1235";
CGSize frameSize = CGSizeMake(300, 50);
UIFont *font = [UIFont systemFontOfSize:12];
CGRect idealFrame = [text boundingRectWithSize:frameSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName:font }
context:nil];
NSLog(#"width: %0.1f", idealFrame.size.width);

Objective C: How to get current UIFont from UIPickerView to calculate NSString length?

For a modular UIPickerView within an UIPopoverController I need to calculate the maximum size (CGRect) of a NSString.
UIPickerView has one component. Values are single line strings.
To calculate the size, I need the current UIFont from the UIPickerView. The following works for UILabel,
UIFont* fontFromUILabel = [UIFont systemFontOfSize:UILabel.font.pointSize];
but what's the equivalent for UIPickerView?
UIFont* fontFromUIPickerViewSelectedState = ????;
float maxWidth = 0.0f;
for(int i = 0; i < [MY_STRING_VALUES count]; i++){
NSString* string = [MY_STRING_VALUES objectAtIndex:i];
CGSize rect = [string sizeWithAttributes:#{
NSFontAttributeName:fontFromUIPickerViewSelectedState }];
if(rect.width > maxWidth){
maxWidth = rect.width;
NSLog(#"new MAX WIDTH: %f",maxWidth);
}
}
//...
//present UIPopoverViewController with calculated size
[_popOver setPopoverContentSize: CGSizeMake(maxWidth,200.0)];
[_popOver presentPopoverFromRect:[(UIButton*)sender frame] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown|UIPopoverArrowDirectionUp animated:TRUE];
And yes, I could get the font from the xib, but this should be a modular solution which works every time!
You can probably use viewForRow:forComponent: to get one of the sub-controls to get its font.

IOS 7 sizeWithFont Deprecated

I cannot seem to replace the deprecated sizeWithFont with boundingRecWithSize correctly. I scoured through all the answers and stayed up all night trying to fix this.I really need help from someone way smarter than I. Here is the code I am trying to modify. Any help would be appreciated.
CGSize sizeForText = [faqItem.answer sizeWithFont:[UIFont boldSystemFontOfSize:14]
constrainedToSize:CGSizeMake(self.tblView.bounds.size.width - padding, MAXFLOAT)
lineBreakMode:NSLineBreakByWordWrapping];
[sectionInfo insertObject:[NSNumber numberWithFloat:roundf(sizeForText.height + 5)]
inRowHeightsAtIndex:0];
You need to use the sizeWithAttributes property.
CGSize mysize = [string sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:14.0f]}];
You can also set it to an already created font size in order to reduce recoding if you use the size more than once:
CGSize mysize = [string sizeWithAttributes:#{NSFontAttributeName: label1.font}];
I do not believe you can use constrainedToSize with this property. It would have to be separately set on a CGRect.
I wrote an sample for you, hope it's helpful.
NSString *text = #" // Do any additional setup after loading the view, typically from a nib.";
CGRect rect = CGRectZero;
NSDictionary *attrDict = #{NSFontAttributeName : [UIFont systemFontOfSize:17]};
rect = [text boundingRectWithSize:CGSizeMake(100,9999)
options:(NSStringDrawingUsesLineFragmentOrigin)
attributes:attrDict
context:Nil];
UILabel *lbl = [[UILabel alloc] init];
lbl.text = text;
rect.origin = CGPointMake(50, 200);
lbl.frame = rect;
lbl.lineBreakMode = NSLineBreakByWordWrapping;
lbl.numberOfLines = 0;
[self.view addSubview:lbl];
lbl.backgroundColor = [UIColor lightGrayColor];
In apple documentation:
sizeWithFont: Returns the size of the string if it were to be rendered
with the specified font on a single line. (Deprecated in iOS 7.0. Use
sizeWithAttributes: instead.)
(CGSize)sizeWithFont:(UIFont *)font Parameters font The font to use for computing the string size. Return Value The width and height of
the resulting string’s bounding box. These values may be rounded up to
the nearest whole number.
So you can use sizeWithAttributes: like this:
CGSize sizeForText = [faqItem.answer sizeWithAttributes:#{NSFontAttributeName:[UIFont boldSystemFontOfSize:14]}
constrainedToSize:CGSizeMake(self.tblView.bounds.size.width - padding, MAXFLOAT)
lineBreakMode:NSLineBreakByWordWrapping];
[sectionInfo insertObject:[NSNumber numberWithFloat:roundf(sizeForText.height + 5)]
inRowHeightsAtIndex:0];

sizeWithFont method is deprecated. boundingRectWithSize returns an unexpected value

In iOS7, sizeWithFont is deprecated, so I am using boundingRectWithSize(which returns a CGRect value). My code:
UIFont *fontText = [UIFont fontWithName:[AppHandlers zHandler].fontName size:16];
// you can use your font.
CGSize maximumLabelSize = CGSizeMake(310, 9999);
CGRect textRect = [myString boundingRectWithSize:maximumLabelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:fontText}
context:nil];
expectedLabelSize = CGSizeMake(textRect.size.width, textRect.size.height);
In textRect, I'm getting a size greater than my maximumLabelSize, a different size than when using sizeWithFont. How can I resolve this issue?
How about create new label and using sizeThatFit:(CGSize)size ??
UILabel *gettingSizeLabel = [[UILabel alloc] init];
gettingSizeLabel.font = [UIFont fontWithName:#"YOUR FONT's NAME" size:16];
gettingSizeLabel.text = #"YOUR LABEL's TEXT";
gettingSizeLabel.numberOfLines = 0;
gettingSizeLabel.lineBreakMode = NSLineBreakByWordWrapping;
CGSize maximumLabelSize = CGSizeMake(310, CGFLOAT_MAX);
CGSize expectSize = [gettingSizeLabel sizeThatFits:maximumLabelSize];
Edit: This upper code is not good for ios 7 and above, so please use below:
CGRect textRect = [myString boundingRectWithSize:maximumLabelSize
options:NSStringDrawingUsesLineFragmentOrigin| NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName:fontText}
context:nil];
Maybe you need to provide additional option to the method that is suggested in this answer:
CGSize maximumLabelSize = CGSizeMake(310, CGFLOAT_MAX);
CGRect textRect = [myString boundingRectWithSize:maximumLabelSize
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName: fontText}
context:nil];
Here is my working code snippet:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:attributeDict];
NSString *headline = [dict objectForKey:#"title"];
UIFont *font = [UIFont boldSystemFontOfSize:18];
CGRect rect = [headline boundingRectWithSize:CGSizeMake(300, 1000) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:#{NSFontAttributeName:font} context:nil];
CGFloat height = roundf(rect.size.height +4)
I added 4px to the calculated height, because without these 4px, there is one line missing.
I use this code snippet in a tableView and add the "height" to an array of NSNumbers and I get the correct cell height for the default textLabel.
Add 4 more pixel if you want more space under the text in the textLabel.
**** UPDATE ****
I do not agree with the "width bug of 40px", I shout be the 4px of missing height, because 4px is the default height of a space between a letter and the bound of a single line.
You can check it with a UILabel, for a fontsize of 16 you need a UILabel height of 20.
But if your last line has no "g" or whatever in it, the measuring could be miss the 4px of height.
I rechecked it with a little method, I get an accurate height of 20,40 or 60
for my label and a right width less than 300px.
To support iOS6 and iOS7, you can use my method:
- (CGFloat)heightFromString:(NSString*)text withFont:(UIFont*)font constraintToWidth:(CGFloat)width
{
CGRect rect;
float iosVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (iosVersion >= 7.0) {
rect = [text boundingRectWithSize:CGSizeMake(width, 1000) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:#{NSFontAttributeName:font} context:nil];
}
else {
CGSize size = [text sizeWithFont:font constrainedToSize:CGSizeMake(width, 1000) lineBreakMode:NSLineBreakByWordWrapping];
rect = CGRectMake(0, 0, size.width, size.height);
}
NSLog(#"%#: W: %.f, H: %.f", self, rect.size.width, rect.size.height);
return rect.size.height;
}
**** UPGRADE ****
Thanks to your comments, I upgraded my function as followed. Since sizeWithFont is deprecated and you will get a warning in XCode, I added the diagnostic-pragma-code to remove the warning for this particular function-call/block of code.
- (CGFloat)heightFromStringWithFont:(UIFont*)font constraintToWidth:(CGFloat)width
{
CGRect rect;
if ([self respondsToSelector:#selector(boundingRectWithSize:options:attributes:context:)]) {
rect = [self boundingRectWithSize:CGSizeMake(width, 1000) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:#{NSFontAttributeName:font} context:nil];
}
else {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
CGSize size = [self sizeWithFont:font constrainedToSize:CGSizeMake(width, 1000) lineBreakMode:NSLineBreakByWordWrapping];
rect = CGRectMake(0, 0, size.width, size.height);
#pragma GCC diagnostic pop
}
return ceil(rect.size.height);
}
In addition to the 4px topic:
depending which font and font-weight you use, the calculation returns different height-values. In my case: HelveticaNeue-Medium with a fontsize of 16.0 returns a line-height of 20.0 for a single line but 39.0 for two lines, 78px for 4 lines --> 1px missing for every line - beginning with line 2 - but you want to have your fontsize + 4px linespace for every line you have to get a height-result.
Please keep that in mind while coding!
I don´t have a function yet for this "problem" but I will update this post when I´m finished.
If I understand correctly, you are using boundingRectWithSize: just as a way of getting the size you would get with sizeWithFont (meaning you want directly the CGSize, not the CGRect)?
This looks like what you are looking for :
Replacement for deprecated sizeWithFont: in iOS 7?
They are using sizeWithAttributes: to get the size, as a replacement for sizeWithFont.
Do you still get the wrong size using something like this :
UIFont *fontText = [UIFont fontWithName:[AppHandlers zHandler].fontName size:16];
// you can use your font.
expectedLabelSize = [myString sizeWithAttributes:#{NSFontAttributeName:fontText}];
The #SoftDesigner's comment has worked for me
CGRect descriptionRect = [description boundingRectWithSize:CGSizeMake(width, 0)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName : [UIFont systemFontOfSize:12]}
context:nil];
result = ceil(descriptionRect.size.height);
for finding size of label run time sizewithfont is deprecated for iOS 7.0 instead of that you have to use -boundingRectWithSize:options:attributes:context: method
you can use it like below code
CGSize constraint = CGSizeMake(MAXIMUM_WIDHT, TEMP_HEIGHT);
NSRange range = NSMakeRange(0, [[self.message body] length]);
NSDictionary *attributes = [YOUR_LABEL.attributedText attributesAtIndex:0 effectiveRange:&range];
CGSize boundingBox = [myString boundingRectWithSize:constraint options:NSStringDrawingUsesFontLeading attributes:attributes context:nil].size;
int numberOfLine = ceil((boundingBox.width) / YOUR_LABEL.frame.size.width);
CGSize descSize = CGSizeMake(ceil(boundingBox.width), ceil(self.lblMessageDetail.frame.size.height*numberOfLine));
CGRect frame=YOUR_LABEL.frame;
frame.size.height=descSize.height;
YOUR_LABEL.frame=frame;
here you have to give width to maximum length for finding height or width.
try this it is working for me.