134

I have this strange issue, and im dealing with it for more than 8 hours now.. Depending on situation i have to calculate UILabels size dynamically,
e.g
my UIViewController receives an event and i change UILabels size. from bigger to smaller. The size of my UILabel gets smaller and i get the correct needed size, but the text in my UILabel stays the same, the same font size and etc. I need the font to get smaller, for the whole text to fit the UILabel. So the question is how to make the text to fit my label with autoshrinking or something like that?

In my xib, UILabels autoshrink is checked, also number of lines is set to 0, and also my string has new line symbols (\n), and i've selected linebreakmode to wordwrap. Maybe anyone was in the same situation as i am now, and could help me? I would really appreciate that.

Thank's in advance!

EDIT: UILabel minimum font size is set to 10

Forge
  • 6,538
  • 6
  • 44
  • 64
Lukas
  • 2,515
  • 4
  • 28
  • 37

17 Answers17

178

In case you are still searching for a better solution, I think this is what you want:

A Boolean value indicating whether the font size should be reduced in order to fit the title string into the label’s bounding rectangle (this property is effective only when the numberOfLines property is set to 1).

When setting this property, minimumScaleFactor MUST be set too (a good default is 0.5).

Swift

var adjustsFontSizeToFitWidth: Bool { get set }

Objective-C

@property(nonatomic) BOOL adjustsFontSizeToFitWidth;

A Boolean value indicating whether spacing between letters should be adjusted to fit the string within the label’s bounds rectangle.

Swift

var allowsDefaultTighteningForTruncation: Bool { get set }

Objective-C

@property(nonatomic) BOOL allowsDefaultTighteningForTruncation;

Source.

Community
  • 1
  • 1
lester
  • 1,826
  • 1
  • 12
  • 7
  • 20
    As a sidenote, `minimumFontSize` is deprecated in iOS 6.0. Use `minimumScaleFactor` instead. – lester Oct 16 '12 at 20:25
  • Hey thanks for your help, but i've tried that and it didn't help. It seems that auto shrink doesn't work for multiple lines. But i don't understand why it is like that.. – Lukas Oct 17 '12 at 11:46
  • 15
    Yes auto shrink doesn't work for multiple lines in UILabel. Try to imagine this: you have text with multiple lines, you want to shrink it so that it "fit" with the width. So should it shrink the whole text to fit in one line, or words stay in the same line and shrink to fit each width? The latter is the most common case, but do not forget that the words is also set to arrange themselves in multiple lines. Even if auto-arrangement of the text is disabled, you will have to face with each lines of the text having different font size, as not every words will fit to the width. – lester Oct 18 '12 at 13:28
  • 2
    I think this answer with a [Category](http://stackoverflow.com/a/11694757/1751195) here is quite good, it might even be what you have implemented anyways. This is definitely something missing in Apple API, or maybe they just don't see shrinking should be a common use case for multiple lines of [static text](http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UILabel_Class/Reference/UILabel.html). – lester Oct 18 '12 at 13:29
  • 1
    well yes that's a good point, but anyway, i think if i set the number of lines to 0, thats kind of a clear statement that there could be one or more lines. And autoshrink could be deterimend by the line numbers i guess.. But anyway, thanks for clearing things out. I thought i was doings something wrong. A categorie is a good idea, i guess i'm going to do that in other project where i will need this. Appreciate your help, good luck ;) – Lukas Oct 19 '12 at 07:48
  • 4
    @lester It should shrink so that the entire text is visible in the number of lines declared for the label given its current dimensions. It doesn't need to attempt to make each line a different size. I'm really surprised it can't figure this out. So if I have a (potentially) long string to display I can *either* have multiple lines or have it auto-size, not both. That's lovely. – devios1 Oct 11 '13 at 19:00
  • 1
    @lester this is pretty easy. Apple's own CoreText library (that drives UIKit) has the feature built-in. The obvious reason it's missing / broken in IB/iOS for multiline labels is that Apple forgot to fix it when they added CoreText to iOS. Their bugtracking is notoriously poor. – Adam Mar 11 '14 at 10:53
  • 1
    adjustsLetterSpacingToFitWidth is deprecated in iOS7 – Matthew Knippen Sep 11 '14 at 21:07
132

This is how I get UILabel Autoshrink work (especially for height of label font fitting in 4s device from 6s Plus Storyboard) in iOS 9.2, Xcode 7.2 ...

enter image description here

  • Number of lines is 0
  • Line Breaks: Clip
  • Autoshrink: Minimum Font Scale 0.25
ohho
  • 50,879
  • 75
  • 256
  • 383
  • 16
    These are the 3 ingredients needed for a label to actually shrink, I was missing the clip line break setting. Also, if its adjacent to other labels (that also might auto shrink) then compression/hugging priorities need to be set or explicit width or height constraints given. This should be the currently accepted answer. – Korey Hinton Dec 31 '15 at 20:15
  • Make sure your label have all the constraints especially leading and trailing. – Mashhadi Aug 19 '16 at 13:51
  • 2
    I have tried with the same options in Xcode8 and found not working. Please help me if anyone has the idea on this. – Ganesh Feb 22 '17 at 08:46
85

minimumFontSize is deprecated in iOS 6.

So use minimumScaleFactor instead of minmimumFontSize.

lbl.adjustsFontSizeToFitWidth = YES
lbl.minimumScaleFactor = 0.5

Swift 5

lbl.adjustsFontSizeToFitWidth = true
lbl.minimumScaleFactor = 0.5
SwiftiSwift
  • 7,528
  • 9
  • 56
  • 96
23

also my solution is the boolean label.adjustsFontSizeToFitWidth = YES; BUT. You must in the interface Builder the Word Wrapping switch to "CLIP". Then autoshrink the Labels. This is very important.

loki-e
  • 239
  • 2
  • 3
  • 3
    Only when I changed from "Word Wrap" to "Clip" did Autoshrink work for me. – NicJ Apr 14 '15 at 02:29
  • When I changed word wrap to clip, I was actually able to get it to resize in a label with zero lines. Boy it'd be nice if this was documented. – DesignatedNerd Dec 17 '15 at 22:15
13

In Swift 3 (Programmatically) I had to do this:

let lbl = UILabel()
lbl.numberOfLines = 0
lbl.lineBreakMode = .byClipping
lbl.adjustsFontSizeToFitWidth = true
lbl.minimumScaleFactor = 0.5
lbl.font = UIFont.systemFont(ofSize: 15)
Naloiko Eugene
  • 2,453
  • 1
  • 28
  • 18
5

You can write like

UILabel *reviews = [[UILabel alloc]initWithFrame:CGRectMake(14, 13,270,30)];//Set frame
reviews.numberOfLines=0;
reviews.textAlignment = UITextAlignmentLeft;
reviews.font = [UIFont fontWithName:@"Arial Rounded MT Bold" size:12];
reviews.textColor=[UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:0.8]; 
reviews.backgroundColor=[UIColor clearColor];

You can calculate number of lines like that

CGSize maxlblSize = CGSizeMake(270,9999);
CGSize totalSize = [reviews.text sizeWithFont:reviews.font 
              constrainedToSize:maxlblSize lineBreakMode:reviews.lineBreakMode];

CGRect newFrame =reviews.frame;
newFrame.size.height = totalSize.height;
reviews.frame = newFrame;

CGFloat reviewlblheight = totalSize.height;

int lines=reviewlblheight/12;//12 is the font size of label

UILabel *lbl=[[UILabel alloc]init];
lbl.frame=CGRectMake(140,220 , 100, 25);//set frame as your requirement
lbl.font=[UIFont fontWithName:@"Arial" size:20];
[lbl setAutoresizingMask:UIViewContentModeScaleAspectFill];
[lbl setLineBreakMode:UILineBreakModeClip];
lbl.adjustsFontSizeToFitWidth=YES;//This is main for shrinking font
lbl.text=@"HelloHelloHello";

Hope this will help you :-) waiting for your reply

Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
Birju
  • 1,132
  • 11
  • 32
4

Well in the end i didn't find my answer. And i think the autoshrink doesn't work for multiple lines. I ended up using suggestion in this link: autoshrink on a UILabel with multiple lines

The solutions is to calulate text height at given width and if text is bigger, to shrink font size and then do he same again until the height is equal or less than your needed size.

I don't understand why this should be so hard to implement. If i'm missing something, everyone is welcome to correct me :)

Community
  • 1
  • 1
Lukas
  • 2,515
  • 4
  • 28
  • 37
  • As per other answers here, the key thing missing was you need to set Word Wrapping to "Clip". It's counter-intuitive, but this is required to get a UILabel to auto-shrink in the way you want. This works with a multi-line UILabel. – Duncan Babbage Apr 21 '18 at 21:25
4

This is for Swift 3 running Xcode 8.2.1 ( 8C1002 )

The best solution that I've found is to set a fixed width in your Storyboard or IB on the label. Set your constraints with constrain to margins. In your viewDidLoad add the following lines of code:

override func viewDidLoad() {
            super.viewDidLoad()

            label.numberOfLines = 1
            label.adjustsFontSizeToFitWidth = true
            label.minimumScaleFactor = 0.5
        }

label constraints to margin

fixed width label constraints to margin

attributes inspector

This worked like a charm and it doesn't overflow to a new line and shrinks the text to fit the width of the label without any weird issues and works in Swift 3.

Rob
  • 338
  • 3
  • 5
4

It's a great question, because it feels like that would be part of the built-in UIKit functionality or a related framework by now. Here's a good visual example of the question:

Font resizing animation

There's no easy way around it, but it's definitely possible. One way to do this is to programmatically try different font sizes until you find one that fits reasonably close to the bounds of the view. You can accomplish this with the boundingRect() function of NSString or NSAttributedString. For example:

let string = "This is a test"
let infiniteSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height:CGFloat.greatestFiniteMagnitude)
let size = string.boundingRect(with: infiniteSize, options: [], attributes: [.font: UIFont.systemFont(ofSize: avgSize)] context: nil).size

You can do a binary search to be more efficient than a complete brute force approach. There are also some considerations that are a bit more involved, including correct word wrapping and iOS font caching performance, if you're looking for something really robust.

If you only care about showing the text on the screen in an easy way, I have developed a robust implementation in Swift, which I'm also using in a production app. It's a UIView subclass with efficient, automatic font scaling for any input text, including multiple lines. To use it, you'd simply do something like:

let view = AKTextView()
// Use a simple or fancy NSAttributedString
view.attributedText = .init(string: "Some text here")
// Add to the view hierarchy somewhere

That's it! You can find the complete source here: https://github.com/FlickType/AccessibilityKit

Hope this helps!

3

In iOS 9 I just had to:

  1. Add constraints from the left and right of the label to the superview.
  2. Set the line break mode to clipping in IB.
  3. Set the number of lines to 1 in IB.

Someone recommended setting number of lines to 0, but for me this just made the label go to multiple lines...

Nick Yap
  • 887
  • 1
  • 10
  • 13
  • This did the trick for me. I had just selected a `UILabel` and a `UITextField` in a static cell and clicked "Resolve AutoLayout Issues", and "Add Missing Constraints". Everything lined up, but Autoshrink needed to know the distance to the label, which "Add Missing Constraints" did not add a constraint for. – Adrian Oct 21 '16 at 01:49
2

Two years on, and this issue is still around...

In iOS 8 / XCode 6.1, I was sometimes finding that my UILabel (created in a UITableViewCell, with AutoLayout turned on, and flexible constraints so it had plenty of space) wouldn't resize itself to fit the text string.

The solution, as in previous years, was to set the text, and then call sizeToFit.

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    . . .
    cell.lblCreatedAt.text = [note getCreatedDateAsString];
    [cell.lblCreatedAt sizeToFit];
}

(Sigh.)

Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159
2

I think you can write bellow code after alloc init Label

UILabel* lbl = [[UILabel alloc]initWithFrame:CGRectMake(0, 10, 280, 50)];
lbl.text = @"vbdsbfdshfisdhfidshufidhsufhdsf dhdsfhdksbf hfsdh fksdfidsf sdfhsd fhdsf sdhfh sdifsdkf ksdhfkds fhdsf dsfkdsfkjdhsfkjdhskfjhsdk fdhsf ";
[lbl setMinimumFontSize:8.0];
[lbl setNumberOfLines:0];
[lbl setFont:[UIFont systemFontOfSize:10.0]];
lbl.lineBreakMode = UILineBreakModeWordWrap;
lbl.backgroundColor = [UIColor redColor];
[lbl sizeToFit];
[self.view addSubview:lbl];

It is working with me fine Use it

iDhaval
  • 7,824
  • 2
  • 21
  • 30
  • 1
    thanks, but this won't calculate or shrink font to minimum needed as i understand, and also why is your minimum font larger then normal? – Lukas Mar 28 '12 at 14:10
1

does not work if numberOfLines > 1 What i did made a condition like this-

if(lblRecLocation.text.length > 100)
    lblRecLocation.font = [UIFont fontWithName:@"app_font_name" size:10];
Vaibhav Saran
  • 12,848
  • 3
  • 65
  • 75
1

In Swift 4 and above:

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200.0, height: 200.0))
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 0
    
label.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
    
view.addSubview(label)
Hadži Lazar Pešić
  • 1,031
  • 1
  • 13
  • 20
1

Here's how to do it.Suppose the following messageLabel is the label you want to have the desired effect.Now,try these simple line of codes:

    //SET THE WIDTH CONSTRAINTS FOR LABEL.
    CGFloat constrainedWidth = 240.0f;//YOU CAN PUT YOUR DESIRED ONE,THE MAXIMUM WIDTH OF YOUR LABEL.
 //CALCULATE THE SPACE FOR THE TEXT SPECIFIED.
    CGSize sizeOfText=[yourText sizeWithFont:yourFont constrainedToSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap];
    UILabel *messageLabel=[[UILabel alloc] initWithFrame:CGRectMake(20,20,constrainedWidth,sizeOfText.height)];
    messageLabel.text=yourText;
    messageLabel.numberOfLines=0;//JUST TO SUPPORT MULTILINING.
Anand
  • 1,129
  • 7
  • 29
  • hey thanks for your answer, but i don't need to calculate the height of label, i have fixed size, and depending on that my font has to get smaller if needed. – Lukas Mar 28 '12 at 14:08
0

Coming late to the party, but since I had the additional requirement of having one word per line, this one addition did the trick for me:

label.numberOfLines = [labelString componentsSeparatedByString:@" "].count;

Apple Docs say:

Normally, the label text is drawn with the font you specify in the font property. If this property is set to YES, however, and the text in the text property exceeds the label’s bounding rectangle, the receiver starts reducing the font size until the string fits or the minimum font size is reached. In iOS 6 and earlier, this property is effective only when the numberOfLines property is set to 1.

But this is a lie. A lie I tell you! It's true for all iOS versions. Specifically, this is true when using a UILabel within a UICollectionViewCell for which the size is determined by constraints adjusted dynamically at runtime via custom layout (ex. self.menuCollectionViewLayout.itemSize = size).

So when used in conjunction with adjustsFontSizeToFitWidth and minimumScaleFactor, as mentioned in previous answers, programmatically setting numberOfLines based on word count solved the autoshrink problem. Doing something similar based on word count or even character count might produce a "close enough" solution.

Justin Whitney
  • 1,242
  • 11
  • 17
-1

Swift 4, Xcode 9.4.1

The solution that worked for me: I had a label within a collection view cell, and the label text was getting trimmed. Set the attributes as below on Storyboard

Lines = 0
LineBreak = Word Wrap
Set yourlabel's leading and trailing constraint = 0 (using Autolayout)
Naishta
  • 11,885
  • 4
  • 72
  • 54