Objective c label.numberOflines is not working [duplicate] - objective-c

Consider I have the following text in a UILabel (a long line of dynamic text):
Since the alien army vastly outnumbers the team, players must use the post-apocalyptic world to their advantage, such as seeking cover behind dumpsters, pillars, cars, rubble, and other objects.
I want to resize the UILabel's height so that the text can fit in. I'm using following properties of UILabel to make the text within to wrap.
myUILabel.lineBreakMode = UILineBreakModeWordWrap;
myUILabel.numberOfLines = 0;
Please let me know if I'm not heading in the right direction. Thanks.

sizeWithFont constrainedToSize:lineBreakMode: is the method to use. An example of how to use it is below:
//Calculate the expected size based on the font and linebreak mode of your label
// FLT_MAX here simply means no constraint in height
CGSize maximumLabelSize = CGSizeMake(296, FLT_MAX);
CGSize expectedLabelSize = [yourString sizeWithFont:yourLabel.font constrainedToSize:maximumLabelSize lineBreakMode:yourLabel.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = yourLabel.frame;
newFrame.size.height = expectedLabelSize.height;
yourLabel.frame = newFrame;

You were going in the right direction. All you need to do is:
myUILabel.numberOfLines = 0;
myUILabel.text = #"Enter large amount of text here";
[myUILabel sizeToFit];

In iOS 6 Apple has added a property to UILabel that greatly simplifies dynamic vertical resizing of labels: preferredMaxLayoutWidth.
Using this property in combination with lineBreakMode = NSLineBreakByWordWrapping and sizeToFit method allows easily resize a UILabel instance to the height that accommodates the entire text.
A quote from iOS documentation:
preferredMaxLayoutWidth
The preferred maximum width (in points) for a multiline label.
Discussion
This property affects the size of the label when layout constraints are applied to it. During layout, if the text extends beyond the width specified by this property, the additional text is flowed to one or more new lines, thereby increasing the height of the label.
A sample:
...
UILabel *status = [[UILabel alloc] init];
status.lineBreakMode = NSLineBreakByWordWrapping;
status.numberOfLines = 5; // limits to 5 lines; use 0 for unlimited.
[self addSubview:status]; // self here is the parent view
status.preferredMaxLayoutWidth = self.frame.size.width; // assumes the parent view has its frame already set.
status.text = #"Some quite lengthy message may go hereā€¦";
[status sizeToFit];
[status setNeedsDisplay];
...

Check this work perfectly without adding Single line of code. (Using Autolayout)
I made a demo for you according to your requirement. Download it from below link,
Autoresize UIView and UILabel
Step by Step Guide :-
Step 1 :- Set constrain to UIView
1) Leading 2) Top 3) Trailing (From mainview)
Step 2 :- Set constrain to Label 1
1) Leading 2) Top 3) Trailing (From it's superview)
Step 3 :- Set constrain to Label 2
1) Leading 2) Trailing (From it's superview)
Step 4 :- Most tricky give botton to UILabel from UIView .
Step 5 :- (Optional) Set constrain to UIButton
1) Leading 2) Bottom 3) Trailing 4) Fixed Height (From mainview)
Output :-
Note :- Make sure you have set Number of lines =0 in Label property.
I hope this info enough to understand Autoresize UIView according to UILabel's height and Autoresize UILabel According to text.

Instead doing this programmatically, you can do this in Storyboard/XIB while designing.
Set UIlabel's number of lines property to 0 in attribute inspector.
Then set width constraint/(or) leading and trailing constraint as per the requirement.
Then set height constraint with minimum value. Finally select the height constraint you added and in the size inspector the one next to attribute inspector, change the height constraint's relation from equal to - greater than.

Thanks guys for help, here is the code I tried which is working for me
UILabel *instructions = [[UILabel alloc]initWithFrame:CGRectMake(10, 225, 300, 180)];
NSString *text = #"First take clear picture and then try to zoom in to fit the ";
instructions.text = text;
instructions.textAlignment = UITextAlignmentCenter;
instructions.lineBreakMode = NSLineBreakByWordWrapping;
[instructions setTextColor:[UIColor grayColor]];
CGSize expectedLabelSize = [text sizeWithFont:instructions.font
constrainedToSize:instructions.frame.size
lineBreakMode:UILineBreakModeWordWrap];
CGRect newFrame = instructions.frame;
newFrame.size.height = expectedLabelSize.height;
instructions.frame = newFrame;
instructions.numberOfLines = 0;
[instructions sizeToFit];
[self addSubview:instructions];

Solution to iOS7 prior and iOS7 above
//
// UILabel+DynamicHeight.m
// For StackOverFlow
//
// Created by Vijay on 24/02/14.
// Copyright (c) 2014 http://Vijay-Apple-Dev.blogspot.com. All rights reserved.
//
#import <UIKit/UIKit.h>
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define iOS7_0 #"7.0"
#interface UILabel (DynamicHeight)
/*====================================================================*/
/* Calculate the size,bounds,frame of the Multi line Label */
/*====================================================================*/
/**
* Returns the size of the Label
*
* #param aLabel To be used to calculte the height
*
* #return size of the Label
*/
-(CGSize)sizeOfMultiLineLabel;
#end
//
// UILabel+DynamicHeight.m
// For StackOverFlow
//
// Created by Vijay on 24/02/14.
// Copyright (c) 2014 http://Vijay-Apple-Dev.blogspot.com. All rights reserved.
//
#import "UILabel+DynamicHeight.h"
#implementation UILabel (DynamicHeight)
/*====================================================================*/
/* Calculate the size,bounds,frame of the Multi line Label */
/*====================================================================*/
/**
* Returns the size of the Label
*
* #param aLabel To be used to calculte the height
*
* #return size of the Label
*/
-(CGSize)sizeOfMultiLineLabel{
NSAssert(self, #"UILabel was nil");
//Label text
NSString *aLabelTextString = [self text];
//Label font
UIFont *aLabelFont = [self font];
//Width of the Label
CGFloat aLabelSizeWidth = self.frame.size.width;
if (SYSTEM_VERSION_LESS_THAN(iOS7_0)) {
//version < 7.0
return [aLabelTextString sizeWithFont:aLabelFont
constrainedToSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
lineBreakMode:NSLineBreakByWordWrapping];
}
else if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(iOS7_0)) {
//version >= 7.0
//Return the calculated size of the Label
return [aLabelTextString boundingRectWithSize:CGSizeMake(aLabelSizeWidth, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{
NSFontAttributeName : aLabelFont
}
context:nil].size;
}
return [self bounds].size;
}
#end

Since sizeWithFont is deprecated I use this one instead.
this one get label specific attributes.
-(CGFloat)heightForLabel:(UILabel *)label withText:(NSString *)text{
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:#{NSFontAttributeName:label.font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){label.frame.size.width, CGFLOAT_MAX}
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
return ceil(rect.size.height);
}

UILabel extension based on this answer for Swift 4 and above
extension UILabel {
func retrieveTextHeight () -> CGFloat {
let attributedText = NSAttributedString(string: self.text!, attributes: [NSFontAttributeName:self.font])
let rect = attributedText.boundingRect(with: CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil)
return ceil(rect.size.height)
}
}
Can be used like:
self.labelHeightConstraint.constant = self.label.retrieveTextHeight()

Here is a category version:
UILabel+AutoSize.h
#import
#interface UILabel (AutoSize)
- (void) autosizeForWidth: (int) width;
#end
UILabel+AutoSize.m
#import "UILabel+AutoSize.h"
#implementation UILabel (AutoSize)
- (void) autosizeForWidth: (int) width {
self.lineBreakMode = UILineBreakModeWordWrap;
self.numberOfLines = 0;
CGSize maximumLabelSize = CGSizeMake(width, FLT_MAX);
CGSize expectedLabelSize = [self.text sizeWithFont:self.font constrainedToSize:maximumLabelSize lineBreakMode:self.lineBreakMode];
CGRect newFrame = self.frame;
newFrame.size.height = expectedLabelSize.height;
self.frame = newFrame;
}
#end

You can implement TableViewController's (UITableViewCell *)tableView:cellForRowAtIndexPath method in the following way (for example) :
#define CELL_LABEL_TAG 1
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *text = #"my long text";
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:identifier] autorelease];
}
CGFloat width = [UIScreen mainScreen].bounds.size.width - 50;
CGFloat height = [self textHeight:text] + 10;
CGRect frame = CGRectMake(10.0f, 10.0f, width, height);
UILabel *cellLabel = [[UILabel alloc] initWithFrame:frame];
cellLabel.tag = CELL_LABEL_TAG;
cellLabel.textColor = [UIColor blackColor];
cellLabel.backgroundColor = [UIColor clearColor];
cellLabel.textAlignment = UITextAlignmentLeft;
cellLabel.font = [UIFont systemFontOfSize:12.0f];
[cell.contentView addSubview:cellLabel];
[cellLabel release];
return cell;
}
UILabel *label = (UILabel *)[cell viewWithTag:CELL_LABEL_TAG];
label.text = text;
label.numberOfLines = 0;
[label sizeToFit];
return cell;
Also use NSString's sizeWithFont:constrainedToSize:lineBreakMode: method to compute the text's height.

My approach to compute the dynamic height of UILabel.
let width = ... //< width of this label
let text = ... //< display content
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.preferredMaxLayoutWidth = width
// Font of this label.
//label.font = UIFont.systemFont(ofSize: 17.0)
// Compute intrinsicContentSize based on font, and preferredMaxLayoutWidth
label.invalidateIntrinsicContentSize()
// Destination height
let height = label.intrinsicContentSize.height
Wrap to function:
func computeHeight(text: String, width: CGFloat) -> CGFloat {
// A dummy label in order to compute dynamic height.
let label = UILabel()
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.font = UIFont.systemFont(ofSize: 17.0)
label.preferredMaxLayoutWidth = width
label.text = text
label.invalidateIntrinsicContentSize()
let height = label.intrinsicContentSize.height
return height
}

And for those that are migrating to iOS 8, here is a class extension for Swift:
extension UILabel {
func autoresize() {
if let textNSString: NSString = self.text {
let rect = textNSString.boundingRectWithSize(CGSizeMake(self.frame.size.width, CGFloat.max),
options: NSStringDrawingOptions.UsesLineFragmentOrigin,
attributes: [NSFontAttributeName: self.font],
context: nil)
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, rect.height)
}
}
}

The easiest and better way that worked for me was to apply height constraint to label and set the priority to low, i.e., (250) in storyboard.
So you need not worry about calculating the height and width programmatically, thanks to storyboard.

Updated Method
+ (CGFloat)heightForText:(NSString*)text font:(UIFont*)font withinWidth:(CGFloat)width {
CGSize constraint = CGSizeMake(width, 20000.0f);
CGSize size;
CGSize boundingBox = [text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:font}
context:nil].size;
size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
return size.height;
}

This is one line of code to get the UILabel Height using Objective-c:
labelObj.numberOfLines = 0;
CGSize neededSize = [labelObj sizeThatFits:CGSizeMake(screenWidth, CGFLOAT_MAX)];
and using .height you will get the height of label as follows:
neededSize.height

You can get height using below code
You have to pass
text 2. font 3. label width
func heightForLabel(text: String, font: UIFont, width: CGFloat) -> CGFloat {
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = text
label.sizeToFit()
return label.frame.height
}

Thanks for this post. It helped me a great deal. In my case I am also editing the text in a separate view controller. I noticed that when I use:
[cell.contentView addSubview:cellLabel];
in the tableView:cellForRowAtIndexPath: method that the label view was continually rendered over the top of the previous view each time I edited the cell. The text became pixelated, and when something was deleted or changed, the previous version was visible under the new version. Here's how I solved the problem:
if ([[cell.contentView subviews] count] > 0) {
UIView *test = [[cell.contentView subviews] objectAtIndex:0];
[test removeFromSuperview];
}
[cell.contentView insertSubview:cellLabel atIndex:0];
No more weird layering. If there is a better way to handle this, Please let me know.

UILabel *itemTitle = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 10,100, 200.0f)];
itemTitle.text = #"aseruy56uiytitfesh";
itemTitle.adjustsFontSizeToFitWidth = NO;
itemTitle.autoresizingMask = UIViewAutoresizingFlexibleWidth;
itemTitle.font = [UIFont boldSystemFontOfSize:18.0];
itemTitle.textColor = [UIColor blackColor];
itemTitle.shadowColor = [UIColor whiteColor];
itemTitle.shadowOffset = CGSizeMake(0, 1);
itemTitle.backgroundColor = [UIColor blueColor];
itemTitle.lineBreakMode = UILineBreakModeWordWrap;
itemTitle.numberOfLines = 0;
[itemTitle sizeToFit];
[self.view addSubview:itemTitle];
use this here all the properties are used on the label and test it by increasing the text in the itemTitle.text as
itemTitle.text = #"diofgorigjveghnhkvjteinughntivugenvitugnvkejrfgnvkhv";
it will show the perfetc answer as you need

You may use it as a method, as well. #Pyjamasam is very much true so i am just making its method. It may be helpfull for some one else
-(CGRect)setDynamicHeightForLabel:(UILabel*)_lbl andMaxWidth:(float)_width{
CGSize maximumLabelSize = CGSizeMake(_width, FLT_MAX);
CGSize expectedLabelSize = [_lbl.text sizeWithFont:_lbl.font constrainedToSize:maximumLabelSize lineBreakMode:_lbl.lineBreakMode];
//adjust the label the the new height.
CGRect newFrame = _lbl.frame;
newFrame.size.height = expectedLabelSize.height;
return newFrame;
}
and just set it like this
label.frame = [self setDynamicHeightForLabel:label andMaxWidth:300.0];

To do this in Swift3 following is the code:
let labelSizeWithFixedWith = CGSize(width: 300, height: CGFloat.greatestFiniteMagnitude)
let exactLabelsize = self.label.sizeThatFits(labelSizeWithFixedWith)
self.label.frame = CGRect(origin: CGPoint(x: 20, y: 20), size: exactLabelsize)

Adding to the above answers:
This can be easily achieved via storyboard.
Set constraint for UILabel.(In my case I did top, left and fixed width)
Set Number of line to 0 in Attribute Inspector
Set Line Break to WordWrap in Attribute Inspector.

One line is Chris's answer is wrong.
newFrame.size.height = maximumLabelSize.height;
should be
newFrame.size.height = expectedLabelSize.height;
Other than that, it's the correct solution.

Finally, it worked. Thank you guys.
I was not getting it to work because i was trying to resize the label in heightForRowAtIndexPath method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
and (yeah silly me), i was resizing the label to default in cellForRowAtIndexPath method - i was overlooking the code i had written earlier:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cellIdentifier = #"myCell";
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell.myUILabel.lineBreakMode = UILineBreakModeWordWrap;
cell.myUILabel.numberOfLines = 0;
cell.myUILabel.text = #"Some very very very very long text....."
[cell.myUILabel.criterionDescriptionLabel sizeToFit];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
CGFloat rowHeight = cell.myUILabel.frame.size.height + 10;
return rowHeight;
}

NSString *str = #"Please enter your text......";
CGSize lblSize = [str sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize: CGSizeMake(200.0f, 600.0f) lineBreakMode: NSLineBreakByWordWrapping];
UILabel *label = [[UILabel alloc]init];
label.frame = CGRectMake(60, 20, 200, lblSize.height);
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.font = [UIFont systemFontOfSize:15];
label.text = str;
label.backgroundColor = [UIColor clearColor];
[label sizeToFit];
[self.view addSubview:label];

My code:
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
label.text = text;
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont fontWithName:_bodyTextFontFamily size:_bodyFontSize];
CGSize size = [label sizeThatFits:CGSizeMake(width, MAXFLOAT)];
float height = size.height;
label.frame = CGRectMake(x, y, width, height);

This method will give perfect height
-(float) getHeightForText:(NSString*) text withFont:(UIFont*) font andWidth:(float) width{
CGSize constraint = CGSizeMake(width , 20000.0f);
CGSize title_size;
float totalHeight;
title_size = [text boundingRectWithSize:constraint
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName : font }
context:nil].size;
totalHeight = ceil(title_size.height);
CGFloat height = MAX(totalHeight, 40.0f);
return height;
}

Swift 2:
yourLabel.text = "your very long text"
yourLabel.numberOfLines = 0
yourLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
yourLabel.frame.size.width = 200
yourLabel.frame.size.height = CGFloat(MAXFLOAT)
yourLabel.sizeToFit()
The interesting lines are sizeToFit() in conjunction with setting a frame.size.height to the max float, this will give room for long text, but sizeToFit() will force it to only use the necessary, but ALWAYS call it after setting the .frame.size.height .
I recommend setting a .backgroundColor for debug purposes, this way you can see the frame being rendered for each case.

myLabel.text = "your very long text"
myLabel.numberOfLines = 0
myLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
Please set constraints for UILable in storyboard including top left bottom right

Related

Change the height of UILabel dynamically based on content

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

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.

Fixing border to UILabel

I am taking text to UILabel which is inside a UItableViewCell(label text is coming from webData-so it varies in size).
I want to give a border for my label, which should fit the text width and height. I have created one, but it's not looking good.
Help me to improve my code.
**Also is there any way to get border with rounded corners ? **
Hey I am getting text inside the border like this, and the corners are not so rounded:
UILabel *cmntBoxlbl = [[UILabel alloc]initWithFrame:CGRectMake(58, 23, 250, 60)];
cmntBoxlbl.font=[UIFont fontWithName:#"Arial" size:12];
cmntBoxlbl.layer.borderColor = [UIColor darkGrayColor].CGColor;
cmntBoxlbl.layer.borderWidth = 1.0;
NSString *text = [NSString stringWithFormat:#"%#%#%#",#" ",[[self.DtlArray objectAtIndex:indexPath.row] objectForKey:#"comment"],#" "];
cmntBoxlbl.text = text;
cmntBoxlbl.textAlignment = UITextAlignmentCenter;
cmntBoxlbl.lineBreakMode = UILineBreakModeWordWrap;
[cmntBoxlbl setTextColor:[UIColor darkGrayColor]];
CGSize expectedLabelSize = [text sizeWithFont:cmntBoxlbl.font
constrainedToSize:cmntBoxlbl.frame.size
lineBreakMode:UILineBreakModeWordWrap];
CGRect newFrame = cmntBoxlbl.frame;
newFrame.size.height = expectedLabelSize.height;
cmntBoxlbl.frame = newFrame;
cmntBoxlbl.numberOfLines = 0;
[cmntBoxlbl sizeToFit];
[cell addSubview:cmntBoxlbl];
*also is there any way to get border with rounded corners ?? *
#import <QuartzCore/QuartzCore.h>
label.layer.borderWidth = 3;
label.layer.borderColor = [[UIColor blackColor] CGColor];
label.layer.cornerRadius = 5;
For rounded corner set.
[cmntBoxlbl.layer setCornerRadius:15];
Also add the QuartzCore framework and import the header:
#import <QuartzCore/QuartzCore.h>

NSString sizeWithFont:constrainedToSize:lineBreakMode: differs from actual Label height

So here how it goes: I'm implementing a UITableView (grouped style, with 1 section) with standard UITableViewCells (style - UITableViewCellStyleSubtitle) as content.
My textLabel.text can be really long, that's why I set some params in TableView:cellForRowAtIndexPath:
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;
cell.textLabel.font = [UIFont boldSystemFontOfSize:CELL_FONT_SIZE];
cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.detailTextLabel.numberOfLines = 0;
[self configureCell:cell atIndexPath:indexPath];
To resize the cell height I've implemented tableView:heightForRowAtIndexPath: as follows:
Test *test = (Test *)[self.frc objectAtIndexPath:indexPath];
NSString *cellText = test.test_name;
UIFont *cellFont = [UIFont boldSystemFontOfSize:CELL_FONT_SIZE];
CGFloat horizontalConstraint;
if (self.bagdePresent)
{
// there will be big badge!
horizontalConstraint = 250.0f;
} else
{
// there will NO badge
horizontalConstraint = 280.0f;
}
CGSize constraintSize = CGSizeMake(horizontalConstraint, MAXFLOAT);
CGSize labelSize = [cellText sizeWithFont:cellFont constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
// return label height, 20 as gap, 30 for rating image,
return labelSize.height + 20 + 30;
First I check if there will be badge image in the cell, if so, I reduce horizontal space available.
And here's the problem (finally): for some strings (some string lengths) sizeWithFont:constrainedToSize:lineBreakMode: returns value that is less than real cell's textLabel height when I set its text property to the same string. As a result some if the cells have textLabels bigger than cell's bounds. When I add\remove some words to\from the string which displayed wrong, sizeWithFont:constrainedToSize:lineBreakMode: gives exact height which is later confirmes with textLabel actual height.
I thought it might be a problem with font, but as you see the font is the same.
Where to dig from here? ;)
UPD1:
Here's an example of textLabel being taller than it was calculated in heightForRowAtIndexPath:
I would recommend to take advantage of sizeToFit. First you set the width of your label to your horizontalConstraint, then you call [cell.textLabel sizeToFit] (that will adjust the height of the label to fit it's content but leave the width as you set it (almost - that means it might become a little narrower):
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat horizontalConstraint;
if (self.bagdePresent)
{
// there will be big badge!
horizontalConstraint = 250.0f;
} else
{
// there will NO badge
horizontalConstraint = 280.0f;
}
Test *test = (Test *)[self.frc objectAtIndexPath:indexPath];
NSString *cellText = test.test_name;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = test.test_name;
UIFont *cellFont = [UIFont boldSystemFontOfSize:CELL_FONT_SIZE];
label.lineBreakMode = UILineBreakModeWordWrap;
label.numberOfLines = 0;
label.font = cellFont;
CGRect frame = label.frame;
frame.size.width = horizontalConstraint;
label.frame = frame;
[label sizeToFit];
CGSize labelSize = label.frame.size;
// return label height, 20 as gap, 30 for rating image,
return labelSize.height + 20 + 30;
}

How to calculate the Width and Height of NSString on UILabel

I am working on a project to make the NSString on UILabel Width and Height dynamically.
I tried with:
NSString *text = [messageInfo objectForKey:#"compiled"];
writerNameLabel.numberOfLines = 0;
writerNameLabel.textAlignment = UITextAlignmentRight;
writerNameLabel.backgroundColor = [UIColor clearColor];
CGSize constraint = CGSizeMake(296,9999);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE]
constrainedToSize:constraint
lineBreakMode:UILineBreakModeWordWrap];
NSLog(#"sizewidth = %f, sizeheight = %f", size.width, size.height);
NSLog(#"writerNameLabel.frame.size.width 1 -> %f",writerNameLabel.frame.size.width);
[writerNameLabel setFrame:CGRectMake(writerNameLabel.frame.origin.x, writerNameLabel.frame.origin.y, size.width, size.height)];
CGRect labelFram = writerNameLabel.frame;
labelFram.origin.x = cell.frame.size.width - writerNameLabel.frame.size.width - 80;
writerNameLabel.frame = labelFram;
NSLog(#"writerNameLabel.frame.size.width 2-> %f",writerNameLabel.frame.size.width);
Please see the green bubble not the grey one.
Still not right.
The code for bubble
bubbleImageView.frame = CGRectMake(writerNameLabel.frame.origin.x, writerNameLabel.frame.origin.y, writerNameLabel.frame.size.width+15, writerNameLabel.frame.size.height+5);
Please Advise! Thanks!
That's because you did not reuse the table cell, the structure should be like:
NSString *text = [messageInfo objectForKey:#"compiled"];
if(cell == nil)
{
writerNameLabel.numberOfLines = 0;
writerNameLabel.textAlignment = UITextAlignmentRight;
writerNameLabel.backgroundColor = [UIColor clearColor];
[cell addSubview:writerNameLabel];
}
else {
writerNameLabel = (UILabel *)[cell viewWithTag:WRITER_NAME_LABEL_TAG];
}
CGSize constraint = CGSizeMake(296,9999);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE]
constrainedToSize:constraint
lineBreakMode:UILineBreakModeWordWrap];
[writerNameLabel setFrame:CGRectMake(writerNameLabel.frame.origin.x, writerNameLabel.frame.origin.y, size.width, size.height)];
I've been gone through and answered some of your question, that's correct way to write your tableview controller. And your problem will be solved.