I'm trying to expand a UITableCell to the size of a UILabel. Here's my code, which doesn't work. Thanks a bunch!
Code:
CGSize constraint = CGSizeMake(labelWidth,9999); // Replace 300 with your label width //TODO replace
NSDictionary *attributes = #{NSFontAttributeName: font};
CGRect rect = [stringValue boundingRectWithSize:constraint
options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:attributes
context:nil];
return rect.size;
I do it in objective-c like this, find swift equivalent. -
CGRect expectedLabelSize = [commentCell.bodyLabel.text
boundingRectWithSize: size
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:attributes context:nil];
return expectedLabelSize.height;
I have a UILabel as subview of UIButton and I am passing the value
from another view and populating in UILabel. Now, I want that UILabel
must change its height based on the content.If text is "Hello" it must
be in 1 line but if text is " my text is too long to fit in the
label", it must change its size. I have used
[self.addressLabel sizeToFit];
But for this i need to leave empty space below UILabel. Simply what I
want is that when text strength increases,size of UILabel and UIView
must expand.
Using below you can get the height of the label
text - text of the label
font - font used in label
width - width of the label
-(float) getHeightForText:(NSString*) text withFont:(UIFont*) font andWidth:(float) width{
CGSize constraint = CGSizeMake(width , 20000.0f);
CGSize title_size;
float totalHeight;
SEL selector = #selector(boundingRectWithSize:options:attributes:context:);
if ([text respondsToSelector:selector]) {
title_size = [text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName : font }
context:nil].size;
totalHeight = ceil(title_size.height);
} else {
title_size = [text sizeWithFont:font
constrainedToSize:constraint
lineBreakMode:NSLineBreakByWordWrapping];
totalHeight = title_size.height ;
}
CGFloat height = MAX(totalHeight, 40.0f);
return height;
}
and create a frame using the height
CGRect frame = questionTitleLbl.frame;
float height = [self getHeightForText:questionTitleLbl.text
withFont:questionTitleLbl.font
andWidth:questionTitleLbl.frame.size.width];
float gap = 2;
cell.questionTitleLbl.frame = CGRectMake(frame.origin.x,
frame.origin.y,
frame.size.width,
height);
Here is the way that i handle this issue:
UILabel *sight = (UILabel *)[cell viewWithTag:100];
sight.text=tmpGroup.title;
sight.frame =CGRectMake(sight.frame.origin.x, sight.frame.origin.y, 191, 21);
sight.font = [UIFont fontWithName:#"RobotoSlab-Bold" size:10];
sight.numberOfLines=0;
sight.lineBreakMode=NSLineBreakByWordWrapping;
[sight sizeToFit];
Use this code its very easy and updated with ios8
add this method to your appconstant file
inline static CGSize getLabelHeightForFont(NSString *fontName,NSString* str, float fontSize, float lblWidth)
{
NSString *text = str;
CGFloat width = lblWidth;
UIFont *font = [UIFont fontWithName:fontName size:fontSize];
NSAttributedString *attributedText =
[[NSAttributedString alloc]
initWithString:text
attributes:#
{
NSFontAttributeName: font
}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
return rect.size;
}
and finally use this code for dynamic create UILabel
CGFloat lbl_height = getLabelHeightForFont(#"System- System", address, 15, lbl_address.frame.size.width);
lbl_address.numberOfLines = 0;
lbl_address.textAlignment = NSTextAlignmentLeft;
[lbl_address setLineBreakMode:NSLineBreakByWordWrapping];
lbl_address.frame = CGRectMake(lbl_address.frame.origin.x, lbl_address.frame.origin.y, lbl_address.frame.size.width, lbl_height+5);
This is the very simplest function for getting dynamic height for labels. You can just use this function.
Here ceil is the predefind function for returns the smallest integer value.
And MAXHEIGHT is maximum height for uilabel for example you can give 1000 also for future caluclations...
-(CGFloat) getHeightForLabels : (UILabel *) label
{
CGSize widthMaxHeight = CGSizeMake(label.frame.size.width, MAXHEIGHT);
CGSize size;
NSStringDrawingContext *context = [[NSStringDrawingContext alloc] init];
CGSize boundingRect = [label.text boundingRectWithSize:widthMaxHeight
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:label.font}
context:context].size;
size = CGSizeMake(ceil(boundingRect.width), ceil(boundingRect.height));
return size.height;
}
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);
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];
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.