By CodeGuy


2011-02-01 16:53:13 8 Comments

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?

12 comments

@Martin Babacaev 2011-02-01 17:03:46

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

@CodeGuy 2011-02-01 17:06:40

but this puts the text all on one line. and if I change the factLabel.numberOfLines, then the font size does not change dynamically.

@Martin Babacaev 2011-02-01 17:10:07

@reising1: you're right. This is just how to make framework to do resizing work for you.

@CodeGuy 2011-02-01 17:12:12

so then the answer to my question is that there is no way to do it using the provided framework?

@Martin Babacaev 2011-02-01 17:17:53

@reising1: see my updated answer

@CodeGuy 2011-02-01 17:27:34

But I don't want to change the size of my Label. I want to keep the label consistent sized and change the size of the font.

@Martin Babacaev 2011-02-01 17:31:03

@reising1: In this case you also can use NSString UIKit addition's method: sizeWithFont:constrainedToSize:lineBreakMode: But this way is a little bit difficult

@CodeGuy 2011-02-01 17:36:37

what would the code look like for this?

@Martin Babacaev 2011-02-01 17:44:01

The only way I can see, is to iterate through different font sizes and compare resulting computed size with label size (or height only). Using sizeWithFont:constrainedToSize:lineBreakMode: of course

@CodeGuy 2011-02-01 17:49:44

Would you mind showing me the code for this? Let's say I have NSString *str and my UILabel has the frame in the code above. Can you show me code that spits out a font size?

@Norbert 2013-09-21 17:11:08

It's deprecated since iOS6. Replace it with myLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];

@dulgan 2014-04-02 15:14:39

XCode tells me labelFontSize doesn't exist, I tried 'factLabel.minimumScaleFactor = 8./self.factLabel.font.pointSize;'

@Jonny Ramos 2014-09-30 20:31:21

@MartinBabacaev numberOfLines = 1 is NOT mandatory, at least in iOS7+.

@user25 2019-03-23 22:18:01

are you joking? adjustsFontSizeToFitWidth only reduces text if it doesn't fit within container

@Devendra Singh 2017-08-04 18:34:15

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)

enter image description here

@videolist 2016-05-30 22:42:57

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

@Amit Singh 2013-02-19 10:47:23

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.

@gnasher729 2015-05-23 13:18:01

I usually use 0.8, because even 0.7 tends to look too small. Of course some text may not fit with minimum scale factor 0.8, it's a matter of deciding what looks better and where things get unreadable. OTOH my apps can be rotated which helps a lot.

@MobileMon 2015-08-11 14:22:39

doesn't work for height, width only

@user25 2019-03-23 22:18:29

adjustsFontSizeToFitWidth only reduces text if it doesn't fit within container

@aaroncatlin 2016-04-10 12:57:16

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

@Phil 2016-03-16 09:27:15

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

@Avi Frankl 2015-12-31 02:47:50

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

@Fattie 2016-05-16 14:49:51

this is incredible.

@Fattie 2016-05-16 15:09:22

Ah - but it doesn't really work with autolayout; the "sizeToFit"s do nothing in that case.

@Colin McDonnell 2016-07-03 21:21:55

this is gorgeous, thank you

@zavtra 2015-06-14 04:16:28

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.

  1. set “Autoshrink” to “Minimum font size.”
  2. set the font to the largest desirable font size (I chose 20)
  3. Change “Line Breaks” from “Word Wrap” to “Truncate Tail.”

Source: http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/

@Evdzhan Mustafa 2016-04-22 00:53:25

saved my life. ty

@bhakti123 2016-07-22 16:44:18

This is truly a life saver!!

@GKK 2016-08-10 06:01:25

Super cool.. That truncate tail point is the most important.. Coz in case of word wrap autolayout doesn't feel the urge to decrease the font size, whereas when it is truncate tail autolayout has to save the text from the blade and it is then that it resizes the font.

@Esqarrouth 2015-06-02 21:21:18

Swift version:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

@Pramod 2016-03-09 13:32:42

Thanks.. Seems like here sequence also maters

@codercat 2012-11-20 04:13:57

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

@Zorayr 2015-02-13 22:50:28

What happens when the size that fits the text is too large for the container's space? For example, let's say you have 100 points available to fit the text view, after calling sizeToFit your textViewA1 becomes 200 points which ends up getting cropped.

@Paulo Miguel Almeida 2014-01-27 13:30:35

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

@Adrian 2014-04-10 16:49:12

it's not working for me. The content of my UILabel is cut off now.

@bmueller 2014-07-07 18:31:41

If it's not working for you, it's probably because the frame of the label isn't set yet. Try setting the frame before calling this (or call setNeedsLayout/layoutIfNeeded if you're using AutoLayout).

@Julius 2014-08-09 20:18:03

Great code! Thanks!

@Mohamed Saleh 2015-05-22 14:46:20

It gives the following crash "' NSInvalidArgumentException', reason: 'NSConcreteAttributedString initWithString:: nil value'"

@Paulo Miguel Almeida 2015-05-23 20:40:09

It means that your NSString can't be nil. I'm assuming that if you want to adjust font size to fill UILabel's content you have at least to provide a text.

@Özgür 2016-02-17 20:02:24

This has a drawback. It line breaks between characters, so you see the words split into different lines. Is there a way to circumvent this?

@Paulo Miguel Almeida 2016-02-18 00:34:22

Hi @Özgür, have you tried to set your label do line break by word wrapping like this ? label.lineBreakMode = .ByWordWrapping

@Özgür 2016-02-18 06:57:42

@PauloMiguelAlmeida yes, I did. I even tried that by adding a NSAttributedString just for this, i.e NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; [style setLineBreakMode:NSLineBreakByWordWrapping];

@Paulo Miguel Almeida 2016-02-18 12:34:11

@Özgür I see. I'll look into this when I get home and I let you know if I managed to solve this.

@schmidt9 2016-12-13 21:25:53

To get accurate result call this method from layoutSubviews of label's superview

@Eyal Ben Dov 2013-06-11 22:41:27

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

@Zorayr 2015-02-13 22:52:21

This seems rather inefficient - you should let the UILabel dynamically height itself to fit in some provided available space. If you run this for something like a table view cell's title font calculation, you will get major lagging issues. The approach may work, but definitely not recommended.

@Jai 2015-03-23 02:47:12

Up-vote for being the only person to actually answer the question.

Related Questions

Sponsored Content

20 Answered Questions

[SOLVED] How do I change the font size of a UILabel in Swift?

  • 2014-06-23 00:04:37
  • Jay
  • 208201 View
  • 222 Score
  • 20 Answer
  • Tags:   ios uilabel swift

32 Answered Questions

[SOLVED] Can I embed a custom font in an iPhone application?

  • 2008-12-11 20:21:11
  • Airsource Ltd
  • 234746 View
  • 768 Score
  • 32 Answer
  • Tags:   ios cocoa-touch fonts

26 Answered Questions

16 Answered Questions

[SOLVED] Set UIButton title UILabel font size programmatically

48 Answered Questions

[SOLVED] Vertically align text to top within a UILabel

0 Answered Questions

Calculating font size on UILabel after adjusting it to fit width

1 Answered Questions

[SOLVED] UILabel in iOS 10, make text font

  • 2017-01-30 23:01:58
  • randombits
  • 186 View
  • 0 Score
  • 1 Answer
  • Tags:   ios iphone swift

4 Answered Questions

[SOLVED] uilabel tail truncation

1 Answered Questions

iOS 7 Autolayout does not correctly resize UILabel when font size is large

2 Answered Questions

UILabel with constant size and dynamic font size depends on text

  • 2012-02-19 13:02:07
  • Valerii Pavlov
  • 1098 View
  • 1 Score
  • 2 Answer
  • Tags:   ios uilabel

Sponsored Content