ChatGPT解决这个技术问题 Extra ChatGPT

UILabel is not auto-shrinking text to fit label size

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

what is the minimum size of your font for lable you set please add.

C
Community

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.


As a sidenote, minimumFontSize is deprecated in iOS 6.0. Use minimumScaleFactor instead.
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.
I think this answer with a Category 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.
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 ;)
@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.
o
ohho

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 ...

https://i.stack.imgur.com/RtcVf.png

Number of lines is 0

Line Breaks: Clip

Autoshrink: Minimum Font Scale 0.25


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.
Make sure your label have all the constraints especially leading and trailing.
I have tried with the same options in Xcode8 and found not working. Please help me if anyone has the idea on this.
S
SwiftiSwift

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

@AntonMatosov, no, it doesn't!
Yes, it does (now) - if you also set the line breaks to Truncate Tail instead of Word Wrap ...
l
loki-e

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.


Only when I changed from "Word Wrap" to "Clip" did Autoshrink work for me.
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.
N
Naloiko Eugene

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)

M
Mick MacCallum

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


so if set number of lines to my label, the text would autoshrink then?
if your content is out of width then it will take nextline
hey i've done like you suggested, but it doesn't work, why the number of lines would help me?
you are right i tried its not useful but i found solution for you do like that my next answer
lbl.adjustsFontSizeToFitWidth=YES; Worked for me!
C
Community

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


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.
R
Rob

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
        }

https://i.stack.imgur.com/kXhch.png

https://i.stack.imgur.com/EnTCx.png

https://i.stack.imgur.com/XTkfc.png

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.


K
Kosta Eleftheriou

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:

https://cdn-images-1.medium.com/max/1600/1*ycfVpMysgKuDgYkwKpuAhA.gif

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!


Nov 2018: Appears the FlickType and AccessibilityKit has gone private or is removed.
N
Nick Yap

In iOS 9 I just had to:

Add constraints from the left and right of the label to the superview. Set the line break mode to clipping in IB. 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...


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.
i
iDhaval

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


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?
M
Mike Gledhill

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


A
Anand

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.

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.
V
Vaibhav Saran

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

H
Hadži Lazar Pešić

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)

J
Justin Whitney

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.


N
Naishta

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)