how to do truncation of text in UILabel - ios

I have problem with truncation of text in UILabel even I have set lineBreakMode = NSLineBreakByWordWrapping perfectly.
Here is my code snippet :
lblSelectedText = [[UILabel alloc] init];
lblSelectedText.numberOfLines = 0;
lblSelectedText.lineBreakMode = NSLineBreakByWordWrapping;
lblSelectedText.font = [UIFont fontWithName:#"MyriadPro-Regular" size:(IS_IPAD_PRO?13.0:9.0)];
lblSelectedText.textAlignment = NSTextAlignmentCenter;
lblSelectedText.textColor = [UIColor lightGrayColor];
lblSelectedText.text = strKey; // Here text will be dynamic
CGFloat width = 150;
CGSize strSize = [self findHeightForText:strKey havingWidth:width andFont:lblSelectedText.font];
lblSelectedText.frame = CGRectMake(12, 10, CGRectGetWidth(aContainerController.view.frame)-20, strSize.height+15);
- (CGSize)findHeightForText:(NSString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font
{
CGSize size = CGSizeZero;
if (text)
{
if (IS_ENGLISH) {
CGRect frame = [text boundingRectWithSize:CGSizeMake(widthValue, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{ NSFontAttributeName:font } context:nil];
size = CGSizeMake(frame.size.width, frame.size.height + 10);
}
else {
CGRect frame = [text boundingRectWithSize:CGSizeMake(widthValue, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:#{ NSFontAttributeName:font } context:nil];
size = CGSizeMake(frame.size.width, frame.size.height + 1);
}
}
return size;
}
Please note that these labels are in collection view cell. Here are some reference images.
If anyone have solution of this problem please share with me...
Thanks.

As you have mentioned in comment that some characters goes in to next line that means there is not enough width to fill every letter in single line thats why it is going to second line as you set number of line to 0(i.e. multiple lines). So there is nothing is wrong in it. It is normal behavior according to your setup made in code.
now if you want that it not goes to second line then set numberOfLines property to 1.
Another option is you can set minimunScaleFactor from 0 to 1 to resize your font to fit in available width.
You can do it something like,
label.adjustsFontSizeToFitWidth = YES;
label.minimumScaleFactor = 0.5f;
So, It will reduce font size to half of the actual to fit it in availabel width.
And once try by changing linebreakmode to NSLineBreakByTruncatingTail also instead of NSLineBreakByWordWrapping.

Check this:-
label.minimumScaleFactor = 0.5f;
label.lineBreakMode = NSLineBreakByTruncatingMiddle;
[label sizeToFit];

Related

boundingRectWithSize gives height with truncated text

I am using the following method to determine the height required to display the text that i have. However i cannot get the correct height without truncating the text at the end.
CGRect labelRect = [comment boundingRectWithSize:headerMax
options:NSStringDrawingUsesLineFragmentOrigin |
NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName:font}
context:nil];
How can i say not to determine the required label height without truncating the text at the end. I want the whole text to fit into the label.
-(CGRect)getLabelSizeWithText:(NSString *)textLabel forFontSize:(CGFloat) fontSize
{
CGRect textRect;
if (textLabel != nil)
{
NSAttributedString *actualString = [[NSAttributedString alloc] initWithString:textLabel attributes:#{NSFontAttributeName:[UIFont fontWithName:#"HelveticaNeue" size:fontSize]} ];
NSStringDrawingContext *targetContext = [[NSStringDrawingContext alloc] init];
CGFloat commentLabelWidth = [[UIScreen mainScreen]bounds].size.width;
textRect = [actualString boundingRectWithSize:CGSizeMake(commentLabelWidth, 900) options:NSStringDrawingUsesLineFragmentOrigin context:targetContext];
}
return textRect;
}
This one should work. Try it
Use -
1. If you want word wrap -
labelView.lineBreakMode = NSLineBreakByWordWrapping;
labelView.numberOfLines = 2;
CGRect labelRect = [comment boundingRectWithSize:headerMax
options:NSStringDrawingUsesLineFragmentOrigin |
NSStringDrawingUsesFontLeading | NSLineBreakByWordWrapping
attributes:#{NSFontAttributeName:font}
context:nil];
2. If you Don't want truncating tail at all -
// No line break mode
labelView.numberOfLines = 0;
CGRect labelRect = [comment boundingRectWithSize:headerMax
options:NSStringDrawingUsesLineFragmentOrigin |
NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName:font}
context:nil];
Try this
 
NSString *cellText = restaurant.review;
UIFont *cellFont = [UIFont systemFontOfSize:14.0];
NSDictionary *attributes = #{NSFontAttributeName: cellFont};
CGRect labelSize = [cellText boundingRectWithSize:CGSizeMake([UIScreen mainScreen].bounds.size.width-20, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
I am not really sure whether I understood your question correctly; it sounds like you are trying to calculate the frame size needed to display the text and setting the frame manually with that. Additionally, by setting .numberOfLines = 2, you are limiting the text that you can show. My suggestion would be for you to set up constraints via autolayout, optionally add a .minimumScaleFactor and let it dynamically resize itself.
If you still want to calculate size, with numberOfLines taken into consideration, you can use a prototype label and have use its height
- (CGSize)preferredSizeForString:(NSString *)string
width:(CGFloat)width
{
UILabel *prototypelabel = [UILabel new];
prototypelabel.frame = CGRectMake(0, 0, width, 0);
prototypelabel.text = #"Your string";
prototypelabel.numberOfLines = 2;
[prototypelabel sizeToFit];
return prototypelabel.frame.size;
}

Set UILabel font size according to its frame size on IOS8?

I found a lot of solution with the property “adjustsFontSizeToFitWidth" , but none of these solutions works on IOS8.
I develop a responsive app and the label font size has to change according to the screen width...
I have :
label = [[UILabel alloc] init];
label.numberOfLines = 1;
label.adjustsFontSizeToFitWidth = YES;
label.font = [UIFont fontWithName:FONT_DIGITAL size:60];
[label setMinimumScaleFactor:8.0/label.font.pointSize];
label.textAlignment = NSTextAlignmentCenter;
label.frame = CGRectMake(0, 0, self.width, self.height);
label.text = #"TEXT";
[self addSubview:valueLabel];
Someone has a solution?
You should set your label's auto shrink property as shown in below image.
Hope this helps.
Following answer is a hack (not the best solution), but it works, so if you want, you can try:
- (void) sizeLabel: (UILabel *) label toRect: (CGRect) labelRect {
// Set the frame of the label to the targeted rectangle
label.frame = labelRect;
// Try all font sizes from largest to smallest font size
CGFloat fontSize = 16.0f;
CGFloat minFontSize = 5.0f;
// Fit label width wize
CGSize constraintSize = CGSizeMake(label.frame.size.width, MAXFLOAT);
do {
// Set current font size
label.font = [UIFont fontWithName:label.font.fontName size:fontSize];
// Find label size for current font size
CGRect textRect = [[label text] boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:label.font}
context:nil];
CGSize labelSize = textRect.size;
// Done, if created label is within target size
if( labelSize.height <= label.frame.size.height )
break;
// Decrease the font size and try again
fontSize -= 1.0f;
} while (fontSize > minFontSize);
}

Putting text in Circle as big as possible

It´s a pretty basic problem but I couldn´t find a proper solution for it. I have several circles which have text in it like you can see in the picture. The text gets loaded dynamically and has a size from one word up to five words or more. The goal is to put the text as big as possible into the circle. New lines can appear but every individual word should stay together. The example image is kind of ok but I would prefer the text to be bigger because there is still some free space between the text and the circle. The circle is 80x80. All solution I tried cropped the text strangly or the text is too small.
How I create the label:
UILabel *buttonlabel = [[UILabel alloc] initWithFrame:CGRectMake(12,7,57,64)];
[buttonlabel setText: #"Recipes"];
buttonlabel.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:18.0f];
buttonlabel.textColor = [UIColor whiteColor];
buttonlabel.textAlignment = NSTextAlignmentCenter;
buttonlabel.lineBreakMode = NSLineBreakByWordWrapping;
buttonlabel.numberOfLines = 3;
[button addSubview:buttonlabel];
[buttonlabel release];
EDIT:
So I tried the solution of Rufel. I think the shrinking kind of works but my words get ripped apart. Even though I have buttonlabel.lineBreakMode = NSLineBreakByWordWrapping;
It looks like this:
This is my code. I also implemented the other methods mentioned in an answer.
//Create the button labels
UILabel *buttonlabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
[buttonlabel setText: #"text";
buttonlabel.textColor = [UIColor whiteColor];
buttonlabel.textAlignment = NSTextAlignmentCenter;
buttonlabel.lineBreakMode = NSLineBreakByWordWrapping;
buttonlabel.numberOfLines = 0;
CGFloat fontSize = 20; // The max font size you want to use
CGFloat labelHeightWithFont = 0;
UIFont *labelFont = nil;
do {
// Trying the current font size if it fits
labelFont = [UIFont systemFontOfSize:fontSize--];
CGRect boundingRect = [self boundingRectForString:subcatbuttontitlesarray[buttonTag-1] font:labelFont];
labelHeightWithFont = boundingRect.size.height;
// Loop until the text at the current size fits the maximum width/height.
} while (labelHeightWithFont > [self buttonLabelMaxWidth]);
buttonlabel.text = subcatbuttontitlesarray[buttonTag-1];
buttonlabel.font = labelFont;
- (CGRect)boundingRectForString:(NSString *)string font:(UIFont *)font
{
return [string boundingRectWithSize:CGSizeMake([self buttonLabelMaxWidth], MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName: font}
context:nil];
}
- (CGFloat)buttonLabelMaxWidth
{
CGFloat hypotenuse = CGRectGetWidth(CGRectMake(0, 0, 60, 60));
CGFloat rightTriangleCathetus = sqrtf((hypotenuse*hypotenuse)/2);
return rightTriangleCathetus;
}
I found this thread here:
iOS7 - Adjusting font size of multiline label to fit its frame
which has the same problem.
Edit 2:
After searching a complete day for the solution and trying all kinds of combinations of the label attributes I somehow figured out that the "numberoflines" is my culprit. So I came up with this dumb solution of counting the words in the string and adjust the number of lines based on the numbers of the string:
NSString *samplestring = #"Three words string";
//Count the words in this string
int times = [[samplestring componentsSeparatedByString:#" "] count]-1;
UILabel *testlabel = [[UILabel alloc]initWithFrame:CGRectMake(30, 30, 60, 60)];
[testlabel setText:samplestring];
[testlabel setFont:[UIFont fontWithName:#"HelveticaNeue-UltraLight" size:40.0f]];
[testlabel setBackgroundColor:[UIColor redColor]];
[testlabel setAdjustsFontSizeToFitWidth:YES];
[testlabel setTextAlignment:NSTextAlignmentCenter];
//My workaround
if(times ==0){
[testlabel setNumberOfLines:1];
}else{
if(times==1){
[testlabel setNumberOfLines:2];
}
else{
[testlabel setNumberOfLines:3];
}}
[self.view addSubview:testlabel];
What you want to do, I think, is to ask the NSString for its boundingRectWithSize:options:attributes:context:. By setting the width of the bounding rect, you can find out what the height would be. You can use the parametric formula for the circle to determine whether that bounding rect fits entirely within the center of the circle. Unfortunately you will have to perform a kind of trial-and-error sequence of approximations, where the text gets larger and larger until the top and bottom stick out of the circle, and then narrow the proposed width and see whether this causes the height to grow too much because the text now wraps an extra time.
Say you have a custom view in which you draw a circle that fits its frame (80x80 in your example).
You will first want to find the maximum width your label can take without letters crossing the circle:
- (CGFloat)buttonLabelMaxWidth
{
CGFloat hypotenuse = CGRectGetWidth(self.bounds);
CGFloat rightTriangleCathetus = sqrtf((hypotenuse*hypotenuse)/2);
return floorf(rightTriangleCathetus);
}
Next, when you pass the title to display, you will want to iterate by decreasing an initially oversized font until the resulting string boundary fits the width previously calculated (which is also the maximum height since it's a circle). UPDATE: You will also want to check every words in the title to be sure they are not being truncated (that they fit the maximum width).
- (void)setButtonTitle:(NSString *)title
{
CGFloat fontSize = 20; // The max font size you want to use
CGFloat minimumFontSize = 5; // The min font size you want to use
CGFloat labelHeightWithFont = 0;
CGFloat longestWordWidth = 0;
UIFont *labelFont = nil;
CGFloat buttonLabelMaxWidth = [self buttonLabelMaxWidth];
do {
if (fontSize < minimumFontSize) {
// Handle exception where the title just won't fit
break;
}
// Trying the current font size if it fits
labelFont = [UIFont systemFontOfSize:fontSize--];
CGSize boundingSize = [self boundingSizeForString:title font:labelFont];
labelHeightWithFont = boundingSize.height;
// Be sure that words are not truncated (that they fits in the maximum width)
longestWordWidth = 0;
for (NSString *word in [title componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
CGSize wordSize = [word sizeWithAttributes:#{NSFontAttributeName: labelFont}];
longestWordWidth = MAX(longestWordWidth, wordSize.width);
}
// Loop until the text at the current size fits the maximum width/height.
} while (labelHeightWithFont > buttonLabelMaxWidth || longestWordWidth > buttonLabelMaxWidth);
self.buttonLabel.text = title;
self.buttonLabel.font = labelFont;
}
- (CGSize)boundingSizeForString:(NSString *)string font:(UIFont *)font
{
CGRect boundingRect = [string boundingRectWithSize:CGSizeMake([self buttonLabelMaxWidth], MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName: font}
context:nil];
return CGSizeMake(ceilf(boundingRect.size.width), ceilf(boundingRect.size.height));
}

Dynamically changing font size of UILabel

I currently have a UILabel:
factLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 280, 100)];
factLabel.text = #"some text some text some text some text";
factLabel.backgroundColor = [UIColor clearColor];
factLabel.lineBreakMode = UILineBreakModeWordWrap;
factLabel.numberOfLines = 10;
[self.view addSubview:factLabel];
Throughout the life of my iOS application, factLabel gets a bunch of different values. Some with multiple sentences, others with just 5 or 6 words.
How can I set up the UILabel so that the font size changes so that the text always fits in the bounds I defined?
Single line:
factLabel.numberOfLines = 1;
factLabel.minimumFontSize = 8;
factLabel.adjustsFontSizeToFitWidth = YES;
The above code will adjust your text's font size down to (for example) 8 trying to fit your text within the label.
numberOfLines = 1 is mandatory.
Multiple lines:
For numberOfLines > 1 there is a method to figure out the size of final text through NSString's sizeWithFont:... UIKit addition methods, for example:
CGSize lLabelSize = [yourText sizeWithFont:factLabel.font
forWidth:factLabel.frame.size.width
lineBreakMode:factLabel.lineBreakMode];
After that you can just resize your label using resulting lLabelSize, for example (assuming that you will change only label's height):
factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, factLabel.frame.size.width, lLabelSize.height);
iOS6
Single line:
Starting with iOS6, minimumFontSize has been deprecated. The line
factLabel.minimumFontSize = 8.;
can be changed to:
factLabel.minimumScaleFactor = 8./factLabel.font.pointSize;
iOS7
Multiple lines:
Starting with iOS7, sizeWithFont becomes deprecated.
Multiline case is reduced to:
factLabel.numberOfLines = 0;
factLabel.lineBreakMode = NSLineBreakByWordWrapping;
CGSize maximumLabelSize = CGSizeMake(factLabel.frame.size.width, CGFLOAT_MAX);
CGSize expectSize = [factLabel sizeThatFits:maximumLabelSize];
factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, expectSize.width, expectSize.height);
iOS 13 (Swift 5):
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
minimumFontSize has been deprecated with iOS 6. You can use minimumScaleFactor.
yourLabel.adjustsFontSizeToFitWidth=YES;
yourLabel.minimumScaleFactor=0.5;
This will take care of your font size according width of label and text.
Single line- There are two ways, you can simply change.
1- Pragmatically (Swift 3)
Just add the following code
yourLabel.numberOfLines = 1;
yourLabel.minimumScaleFactor = 0.7;
yourLabel.adjustsFontSizeToFitWidth = true;
2 - Using UILabel Attributes inspector
i- Select your label- Set number of lines 1.
ii- Autoshrink- Select Minimum Font Scale from drop down
iii- Set Minimum Font Scale value as you wish , I have set 0.7 as in below image. (default is 0.5)
Based on #Eyal Ben Dov's answer you may want to create a category to make it flexible to use within another apps of yours.
Obs.: I've updated his code to make compatible with iOS 7
-Header file
#import <UIKit/UIKit.h>
#interface UILabel (DynamicFontSize)
-(void) adjustFontSizeToFillItsContents;
#end
-Implementation file
#import "UILabel+DynamicFontSize.h"
#implementation UILabel (DynamicFontSize)
#define CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE 35
#define CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE 3
-(void) adjustFontSizeToFillItsContents
{
NSString* text = self.text;
for (int i = CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE; i>CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE; i--) {
UIFont *font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:#{NSFontAttributeName: font}];
CGRect rectSize = [attributedText boundingRectWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil];
if (rectSize.size.height <= self.frame.size.height) {
self.font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
break;
}
}
}
#end
-Usage
#import "UILabel+DynamicFontSize.h"
[myUILabel adjustFontSizeToFillItsContents];
Cheers
It's 2015. I had to go to find a blog post that would explain how to do it for the latest version of iOS and XCode with Swift so that it would work with multiple lines.
set “Autoshrink” to “Minimum font size.”
set the font to the largest desirable font size (I chose 20)
Change “Line Breaks” from “Word Wrap” to “Truncate Tail.”
Source:
http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/
Swift version:
textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5
Here's a Swift extension for UILabel. It runs a binary search algorithm to resize the font based off the width and height of the label's bounds. Tested to work with iOS 9 and autolayout.
USAGE: Where <label> is your pre-defined UILabel that needs font resizing
<label>.fitFontForSize()
By Default, this function searches in within the range of 5pt and 300pt font sizes and sets the font to fit its text "perfectly" within the bounds (accurate within 1.0pt). You could define the parameters so that it, for example, searches between 1pt and the label's current font size accurately within 0.1pts in the following way:
<label>.fitFontForSize(1.0, maxFontSize: <label>.font.pointSize, accuracy:0.1)
Copy/Paste the following code into your file
extension UILabel {
func fitFontForSize(var minFontSize : CGFloat = 5.0, var maxFontSize : CGFloat = 300.0, accuracy : CGFloat = 1.0) {
assert(maxFontSize > minFontSize)
layoutIfNeeded() // Can be removed at your own discretion
let constrainedSize = bounds.size
while maxFontSize - minFontSize > accuracy {
let midFontSize : CGFloat = ((minFontSize + maxFontSize) / 2)
font = font.fontWithSize(midFontSize)
sizeToFit()
let checkSize : CGSize = bounds.size
if checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width {
minFontSize = midFontSize
} else {
maxFontSize = midFontSize
}
}
font = font.fontWithSize(minFontSize)
sizeToFit()
layoutIfNeeded() // Can be removed at your own discretion
}
}
NOTE: Each of the layoutIfNeeded() calls can be removed at your own discretion
Its a little bit not sophisticated but this should work,
for example lets say you want to cap your uilabel to 120x120, with max font size of 28:
magicLabel.numberOfLines = 0;
magicLabel.lineBreakMode = NSLineBreakByWordWrapping;
...
magicLabel.text = text;
for (int i = 28; i>3; i--) {
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:(CGFloat)i] constrainedToSize:CGSizeMake(120.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
if (size.height < 120) {
magicLabel.font = [UIFont systemFontOfSize:(CGFloat)i];
break;
}
}
Just send the sizeToFit message to the UITextView. It will adjust its own height to just fit its text. It will not change its own width or origin.
[textViewA1 sizeToFit];
Swift 2.0 Version:
private func adapteSizeLabel(label: UILabel, sizeMax: CGFloat) {
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
let maximumLabelSize = CGSizeMake(label.frame.size.width, sizeMax);
let expectSize = label.sizeThatFits(maximumLabelSize)
label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, expectSize.width, expectSize.height)
}
This solution works for multiline:
After following several articles, and requiring a function that would automatically scale the text and adjust the line count to best fit within the given label size, I wrote a function myself. (ie. a short string would fit nicely on one line and use a large amount of the label frame, whereas a long strong would automatically split onto 2 or 3 lines and adjust the size accordingly)
Feel free to re-use it and tweak as required. Make sure you call it after viewDidLayoutSubviews has finished so that the initial label frame has been set.
+ (void)setFontForLabel:(UILabel *)label withMaximumFontSize:(float)maxFontSize andMaximumLines:(int)maxLines {
int numLines = 1;
float fontSize = maxFontSize;
CGSize textSize; // The size of the text
CGSize frameSize; // The size of the frame of the label
CGSize unrestrictedFrameSize; // The size the text would be if it were not restricted by the label height
CGRect originalLabelFrame = label.frame;
frameSize = label.frame.size;
textSize = [label.text sizeWithAttributes:#{NSFontAttributeName:[UIFont systemFontOfSize: fontSize]}];
// Work out the number of lines that will need to fit the text in snug
while (((textSize.width / numLines) / (textSize.height * numLines) > frameSize.width / frameSize.height) && (numLines < maxLines)) {
numLines++;
}
label.numberOfLines = numLines;
// Get the current text size
label.font = [UIFont systemFontOfSize:fontSize];
textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName : label.font}
context:nil].size;
// Adjust the frame size so that it can fit text on more lines
// so that we do not end up with truncated text
label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, label.frame.size.width, label.frame.size.width);
// Get the size of the text as it would fit into the extended label size
unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;
// Keep reducing the font size until it fits
while (textSize.width > unrestrictedFrameSize.width || textSize.height > frameSize.height) {
fontSize--;
label.font = [UIFont systemFontOfSize:fontSize];
textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:#{NSFontAttributeName : label.font}
context:nil].size;
unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;
}
// Set the label frame size back to original
label.frame = originalLabelFrame;
}
Here is the fill code of a UILabel subclass that implements animated font size change:
#interface SNTextLayer : CATextLayer
#end
#implementation SNTextLayer
- (void)drawInContext:(CGContextRef)ctx {
// We override this to make text appear at the same vertical positon as in UILabel
// (otherwise it's shifted tdown)
CGFloat height = self.bounds.size.height;
float fontSize = self.fontSize;
// May need to adjust this somewhat if it's not aligned perfectly in your implementation
float yDiff = (height-fontSize)/2 - fontSize/10;
CGContextSaveGState(ctx);
CGContextTranslateCTM(ctx, 0.0, yDiff);
[super drawInContext:ctx];
CGContextRestoreGState(ctx);
}
#end
#interface SNAnimatableLabel ()
#property CATextLayer* textLayer;
#end
#interface SNAnimatableLabel : UILabel
- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration;
#end
#implementation SNAnimatableLabel
- (void)awakeFromNib {
[super awakeFromNib];
_textLayer = [SNTextLayer new];
_textLayer.backgroundColor = self.backgroundColor.CGColor;
_textLayer.foregroundColor = self.textColor.CGColor;
_textLayer.font = CGFontCreateWithFontName((CFStringRef)self.font.fontName);
_textLayer.frame = self.bounds;
_textLayer.string = self.text;
_textLayer.fontSize = self.font.pointSize;
_textLayer.contentsScale = [UIScreen mainScreen].scale;
[_textLayer setPosition: CGPointMake(CGRectGetMidX(_textLayer.frame), CGRectGetMidY(_textLayer.frame))];
[_textLayer setAnchorPoint: CGPointMake(0.5, 0.5)];
[_textLayer setAlignmentMode: kCAAlignmentCenter];
self.textColor = self.backgroundColor;
// Blend text with background, so that it doens't interfere with textlayer text
[self.layer addSublayer:_textLayer];
self.layer.masksToBounds = NO;
}
- (void)setText:(NSString *)text {
_textLayer.string = text;
super.text = text;
}
- (void)layoutSubviews {
[super layoutSubviews];
// Need to enlarge the frame, otherwise the text may get clipped for bigger font sizes
_textLayer.frame = CGRectInset(self.bounds, -5, -5);
}
- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration {
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
_textLayer.fontSize = fontSize;
[CATransaction commit];
}

How to calculate UILabel width based on text length?

I want to display an image next to a UILabel, however UILabel has variable text length, so I don't know where to place the image. How can I accomplish this?
CGSize expectedLabelSize = [yourString sizeWithFont:yourLabel.font
constrainedToSize:maximumLabelSize
lineBreakMode:yourLabel.lineBreakMode];
What is -[NSString sizeWithFont:forWidth:lineBreakMode:] good for?
this question might have your answer, it worked for me.
For 2014, I edited in this new version, based on the ultra-handy comment by Norbert below! This does everything.
// yourLabel is your UILabel.
float widthIs =
[self.yourLabel.text
boundingRectWithSize:self.yourLabel.frame.size
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{ NSFontAttributeName:self.yourLabel.font }
context:nil]
.size.width;
NSLog(#"the width of yourLabel is %f", widthIs);
yourLabel.intrinsicContentSize.width for Objective-C / Swift
In swift
yourLabel.intrinsicContentSize().width
The selected answer is correct for iOS 6 and below.
In iOS 7, sizeWithFont:constrainedToSize:lineBreakMode: has been deprecated. It is now recommended you use boundingRectWithSize:options:attributes:context:.
CGRect expectedLabelSize = [yourString boundingRectWithSize:sizeOfRect
options:<NSStringDrawingOptions>
attributes:#{
NSFontAttributeName: yourString.font
AnyOtherAttributes: valuesForAttributes
}
context:(NSStringDrawingContext *)];
Note that the return value is a CGRect not a CGSize. Hopefully that'll be of some assistance to people using it in iOS 7.
Swift 4 Answer who are using Constraint
label.text = "Hello World"
var rect: CGRect = label.frame //get frame of label
rect.size = (label.text?.size(attributes: [NSFontAttributeName: UIFont(name: label.font.fontName , size: label.font.pointSize)!]))! //Calculate as per label font
labelWidth.constant = rect.width // set width to Constraint outlet
Swift 5 Answer who are using Constraint
label.text = "Hello World"
var rect: CGRect = label.frame //get frame of label
rect.size = (label.text?.size(withAttributes: [NSAttributedString.Key.font: UIFont(name: label.font.fontName , size: label.font.pointSize)!]))! //Calculate as per label font
labelWidth.constant = rect.width // set width to Constraint outlet
In iOS8 sizeWithFont has been deprecated, please refer to
CGSize yourLabelSize = [yourLabel.text sizeWithAttributes:#{NSFontAttributeName : [UIFont fontWithName:yourLabel.font size:yourLabel.fontSize]}];
You can add all the attributes you want in sizeWithAttributes.
Other attributes you can set:
- NSForegroundColorAttributeName
- NSParagraphStyleAttributeName
- NSBackgroundColorAttributeName
- NSShadowAttributeName
and so on. But probably you won't need the others
CGRect rect = label.frame;
rect.size = [label.text sizeWithAttributes:#{NSFontAttributeName : [UIFont fontWithName:label.font.fontName size:label.font.pointSize]}];
label.frame = rect;
Here's something I came up with after applying a few principles other SO posts, including Aaron's link:
AnnotationPin *myAnnotation = (AnnotationPin *)annotation;
self = [super initWithAnnotation:myAnnotation reuseIdentifier:reuseIdentifier];
self.backgroundColor = [UIColor greenColor];
self.frame = CGRectMake(0,0,30,30);
imageView = [[UIImageView alloc] initWithImage:myAnnotation.THEIMAGE];
imageView.frame = CGRectMake(3,3,20,20);
imageView.layer.masksToBounds = NO;
[self addSubview:imageView];
[imageView release];
CGSize titleSize = [myAnnotation.THETEXT sizeWithFont:[UIFont systemFontOfSize:12]];
CGRect newFrame = self.frame;
newFrame.size.height = titleSize.height + 12;
newFrame.size.width = titleSize.width + 32;
self.frame = newFrame;
self.layer.borderColor = [UIColor colorWithRed:0 green:.3 blue:0 alpha:1.0f].CGColor;
self.layer.borderWidth = 3.0;
UILabel *infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(26,5,newFrame.size.width-32,newFrame.size.height-12)];
infoLabel.text = myAnnotation.title;
infoLabel.backgroundColor = [UIColor clearColor];
infoLabel.textColor = [UIColor blackColor];
infoLabel.textAlignment = UITextAlignmentCenter;
infoLabel.font = [UIFont systemFontOfSize:12];
[self addSubview:infoLabel];
[infoLabel release];
In this example, I'm adding a custom pin to a MKAnnotation class that resizes a UILabel according to the text size. It also adds an image on the left side of the view, so you see some of the code managing the proper spacing to handle the image and padding.
The key is to use CGSize titleSize = [myAnnotation.THETEXT sizeWithFont:[UIFont systemFontOfSize:12]]; and then redefine the view's dimensions. You can apply this logic to any view.
Although Aaron's answer works for some, it didn't work for me. This is a far more detailed explanation that you should try immediately before going anywhere else if you want a more dynamic view with an image and resizable UILabel. I already did all the work for you!!

Resources