Resize UILabel with sizeWithFont:constrainedToSize:lineBreakMode: deprecated in iOS7 - resize

If the sizeWithFont:constrainedToSize:lineBreakMode: method is deprecated in iOS7, how can I automatically resize a UILabel to dynamically adjust its height and width to fit the text?

I ended up using this. Works for me. This does not work with IBOutlets object but useful when computing dynamically the height of the text on uitableview's heightForRowAtIndexPath: method.
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"FontName" size:15], NSFontAttributeName,
nil];
CGRect frame = [label.text boundingRectWithSize:CGSizeMake(263, 2000.0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributesDictionary
context:nil];
CGSize size = frame.size;

This should work in iOS6 and iOS7, but will break your label constraints (you need to set them all back programatically if needed):
-(void)resizeHeightForLabel: (UILabel*)label {
label.numberOfLines = 0;
UIView *superview = label.superview;
[label removeFromSuperview];
[label removeConstraints:label.constraints];
CGRect labelFrame = label.frame;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
CGRect expectedFrame = [label.text boundingRectWithSize:CGSizeMake(label.frame.size.width, 9999)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
label.font, NSFontAttributeName,
nil]
context:nil];
labelFrame.size = expectedFrame.size;
labelFrame.size.height = ceil(labelFrame.size.height); //iOS7 is not rounding up to the nearest whole number
} else {
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
labelFrame.size = [label.text sizeWithFont:label.font
constrainedToSize:CGSizeMake(label.frame.size.width, 9999)
lineBreakMode:label.lineBreakMode];
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
}
label.frame = labelFrame;
[superview addSubview:label];
}
Add this method to your viewController and use it like this:
[self resizeHeightForLabel:myLabel];
//set new constraints here if needed

Related

Dynamic collection view cell size-objective c?

I am working on collection view in objective c,
My problem was
1.I want to change the cell size according to it's content size
2.If there are no image in cell then like and comment view should go above(refer image).
I have changed the constraint like
NSLayoutConstraint *newConstraint;
if([image isEqualToString:#"no_image.jpg"] || [image isEqualToString:#"no_image.jpg"]){
cell.desc_ImgViewWidth.constant = 0;
[cell.Descimgview setHidden:YES];
newConstraint = [NSLayoutConstraint constraintWithItem:(cell.bottomConstraint).firstItem attribute:(cell.bottomConstraint).firstAttribute relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:(cell.bottomConstraint).secondItem attribute:(cell.bottomConstraint).secondAttribute multiplier:(cell.bottomConstraint).multiplier constant:(cell.bottomConstraint).constant];
}
else{
cell.desc_ImgViewWidth.constant = 120;
[cell.Descimgview setHidden:NO];
newConstraint = [NSLayoutConstraint constraintWithItem:(cell.bottomConstraint).firstItem attribute:(cell.bottomConstraint).firstAttribute relatedBy:NSLayoutRelationEqual toItem:(cell.bottomConstraint).secondItem attribute:(cell.bottomConstraint).secondAttribute multiplier:(cell.bottomConstraint).multiplier constant:(cell.bottomConstraint).constant];
}
[cell.contentView removeConstraint:(cell.bottomConstraint)];
[cell.contentView addConstraint:newConstraint];
[cell layoutIfNeeded];
[[cell contentView] setFrame:[cell bounds]];
[[cell contentView] setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
in cellForItemAtIndexPath delegate method.(like and comment view moving above at first, but after reloading the cell again i.e, like scrolling etc the constraint is not working perfectly)
I want to move like and comment view like this and to reduce the cell height for that particular cell(refer below image)
How to properly do this?
You can use UICollectionViewLayout
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if imageView != nil
{
return CGSizeMake(width, height)
}
else
{
return CGSizeMake(width, height)
}
}
Finally achieved like this,
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary* object = [_Data objectAtIndex:indexPath.item];
NSString *image = [object valueForKey:#"image"];
if([image isEqualToString:#"no_image.jpg"] || [image isEqualToString:#"no_image.jpg"]){
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
float cellWidth = screenWidth;
NSDictionary* object = [_Data objectAtIndex:indexPath.item];
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:[SwiftHelper getEmojiText:[object valueForKey:#"description"]]];
[str addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"Georgia" size:15.0] range:NSMakeRange(0, str.length)];
CGSize sizeName = CGRectIntegral([str boundingRectWithSize:CGSizeMake(cellWidth-8, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil]).size;
NSMutableAttributedString *str1 = [[NSMutableAttributedString alloc] initWithString:[SwiftHelper getEmojiText:[object valueForKey:#"name"]]];
[str1 addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"Georgia" size:15.0] range:NSMakeRange(0, str1.length)];
CGSize sizeName1 = CGRectIntegral([str1 boundingRectWithSize:CGSizeMake(cellWidth-8, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil]).size;
NSLog(#"%f,%f",sizeName.height,sizeName1.height);
if(sizeName.height > 100){
sizeName.height = 70;
}
return CGSizeMake(cellWidth, sizeName.height + sizeName1.height + 110);
}
else{
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
float cellWidth = screenWidth;
CGSize size = CGSizeMake(cellWidth, 182);
return size;
}
Thank you for your suggestions friends.
Really it's better to use tableview for this but unfortunately after using collection view for a previous design the requirement changed like this. So, I struggled.

UIlabel height issue in Custom UItableViewCell in iOS 6 and iOS 7

i am displaying different items in custom table cell. when i try to display multiline uilabel text. it si only showing single line. even though i calculate the label height
also i try to set new frame with new height after calculation.
my code is as follows:
+(CGRect )getlabelHeight:(CGRect)frame withFontName:(NSString *)font andFontSize:(float)fontSize andText:(NSString *)text
{
CGSize constrainedSize = CGSizeMake(frame.size.width, 9999);
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:font size:fontSize], NSFontAttributeName,
nil];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:text attributes:attributesDictionary];
CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];
if (requiredHeight.size.width > constrainedSize.width) {
requiredHeight = CGRectMake(0,0, constrainedSize.width, requiredHeight.size.height);
}
CGRect newFrame = frame;
newFrame.size.height = requiredHeight.size.height;
frame = newFrame;
return frame;
}
my tabelview method is as follows:
CGRect descFrame=[Utility getlabelHeight:CGRectMake(65,35+titleFrame.size.height, 250, 20) withFontName:appFont andFontSize:15 andText:[[allComments objectAtIndex:indexPath.row]valueForKey:#"comment"]];
NSLog(#"%# \n %f",[[allComments objectAtIndex:indexPath.row]valueForKey:#"comment"],descFrame.size.height);
[cell.title setFont:[UIFont fontWithName:appFont size:15]];
[cell.title setText:[[allComments objectAtIndex:indexPath.row]valueForKey:#"comment"]];
[cell.title setLineBreakMode:NSLineBreakByWordWrapping];
[cell.title setNumberOfLines:0];
[cell.title setFrame:descFrame];
[cell.title setTextColor:[UIColor blackColor]];
Please help me!!1 i am screwed...
thanks you!!!

deprecated ’sizeWithFont:constrainedToSize:' replacement

Somebody knows how to use -boundingRectWithSize:options:attributes:context: as a replacement of the deprecated ”sizeWithFont:constrainedToSize:” method in this case.
CGSize labelSize = [self.mainLabel.text sizeWithFont:self.mainLabel.font constrainedToSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds))];
Gets the warning: 'sizeWithFont:constrainedToSize:' is deprecated: first deprecated in iOS 7.0 - Use -boundingRectWithSize:options:attributes:context:
This is the hole code piece:
// calculate the label size
CGSize labelSize = [self.mainLabel.text sizeWithFont:self.mainLabel.font constrainedToSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds))];
each_object(self.labels, ^(UILabel *label) {
CGRect frame = label.frame;
frame.origin.x = offset;
frame.size.height = CGRectGetHeight(self.bounds);
frame.size.width = labelSize.width + 2.f /*Magic number*/;
label.frame = frame;
// Recenter label vertically within the scroll view
label.center = CGPointMake(label.center.x, roundf(self.center.y - CGRectGetMinY(self.frame)));
offset += CGRectGetWidth(label.bounds) + self.labelSpacing;
});
At the moment you have...
CGSize labelSize = [self.mainLabel.text sizeWithFont:self.mainLabel.font constrainedToSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds))];
So use...
CGRect boundingRect = [self.mainLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds))
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
CGSize labelSize = boundingRect.size;
That should work.
Or... with attributes...
CGRect boundingRect = [self.mainLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds))
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:self.mainLabel.font}
context:nil];
For example this way
-(CGFloat)getLabelSize:(UILabel *)label fontSize:(NSInteger)fontSize
{
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont systemFontOfSize:fontSize], NSFontAttributeName,
nil];
CGRect frame = [label.text boundingRectWithSize:CGSizeMake(270, 2000.0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributesDictionary
context:nil];
CGSize size = frame.size;
return size.height;
}

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.

Replacement for deprecated sizeWithFont: in iOS 7?

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.