Can't get UITextField to autoshrink text

25,866

Solution 1

I used the answer posted by @Purva as a starting point to come up with this method that gives the required font size starting at the configured font size, and not to drop below the configured minimum font size. Whereas @Purva tested for the height of the text I required the width to fit. This method can be put in either a category or a subclass of UITextField. I have it in a subclass which also captures the UITextFieldTextDidChangeNotification. From this notification handler I call the new method and resize the font if required. I also call it when I am assigning new text to the textfield to make sure it will fit by subclassing - (void)setText: (NSString*)text.

In each case, when I call it I am using the following code:

    CGFloat requiredFontSize = self.requiredFontSize;
    if( self.font.pointSize != requiredFontSize )
    {
        self.font = [self.font fontWithSize: requiredFontSize];
    }

Here is the new method:

- (CGFloat)requiredFontSize
{
    const CGRect  textBounds = [self textRectForBounds: self.frame];
    const CGFloat maxWidth   = textBounds.size.width;

    if( _originalFontSize == -1 ) _originalFontSize = self.font.pointSize;

    UIFont* font     = self.font;
    CGFloat fontSize = _originalFontSize;

    BOOL found = NO;
    do
    {
        if( font.pointSize != fontSize )
        {
            font = [font fontWithSize: fontSize];
        }

        CGSize size = [self.text sizeWithFont: font];
        if( size.width <= maxWidth )
        {
            found = YES;
            break;
        }

        fontSize -= 1.0;
        if( fontSize < self.minimumFontSize )
        {
            fontSize = self.minimumFontSize;
            break;
        }

    } while( TRUE );

    return( fontSize );
}

Solution 2

I had the same problem. The UITextField stoped shrinking the text when it was too long, but instead it resized itself and grew outside its 'bounds'.

The solution that helped me was to set width constraint on given UITextField. After that it did not grew anymore, instead the text inside got smaller as intended. (Of course you have to set minFontSize and check the "adjust to fit" box in storyboard.)

I know it's kind of a late, but if anyone else will find this question via google as I did...it might just help.

Solution 3

I think you want to set the adjustsFontSizeToFitWidth property for your UITextField to YES, and specify a minimumFontSize. Works for me, but I add the UITextField programmatically - IB issue?

Solution 4

Swift 3.0 way to resize the font to fit:

let startFontSize = 14.0
let minFontSize = 7.0

func resizeFont() {
    guard let font = self.font, let text = self.text else {
        return
    }

    let textBounds = self.textRect(forBounds: self.bounds)
    let maxWidth = textBounds.size.width

    for fontSize in stride(from: startFontSize, through: minFontSize, by: -0.5) {
        let size = (text as NSString).size(attributes: [NSFontAttributeName: font.withSize(CGFloat(fontSize))])
        self.font = font.withSize(CGFloat(fontSize))
        if size.width <= maxWidth {
            break
        }
    }
}

Solution 5

Try this its working for me (swift 3.0)

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // bla bla ... anything inside method you want to do
        // reset font size of textfield
        textField.font = UIFont.systemFont(ofSize: CGFloat(15.0))
        var widthOfText: CGFloat = textField.text!.size(attributes: [NSFontAttributeName: textField.font!]).width
        var widthOfFrame: CGFloat = textField.frame.size.width
        // decrease font size until it fits (25 is constant that works for me)
        while (widthOfFrame - 25) < widthOfText {
            let fontSize: CGFloat = textField.font!.pointSize
            textField.font = textField.font?.withSize(CGFloat(fontSize - 0.5))
            widthOfText = (textField.text?.size(attributes: [NSFontAttributeName: textField.font!]).width)!
            widthOfFrame = textField.frame.size.width
        }
        return true
    }
Share:
25,866

Related videos on Youtube

hvanbrug
Author by

hvanbrug

Software developer and all around nerd

Updated on March 25, 2020

Comments

  • hvanbrug
    hvanbrug about 4 years

    I have a UITextField on a table view cell, and when it's text becomes too long I would like the font size to decrease. I want to make it very clear that I am talking about a UITextField, not a UILabel or a UITextView. The reason I say this is because I have seen this question pop up several times and the answers were all based on UILabel instead of UITextField. For example, someone asked "I can't get my UITextField to autoshrink" and the answer was "make sure it's numberOfLines is set to 1". To the best of my knowledge, a UITextField does not even have that property and is a single line control.

    I have tried:

    • in IB setting the font to system 14.0, minFontSize to 7 and checking the "adjust to fit" box
    • in code in tableView:cellForRowAtIndexPath:
    ptCell.name.font = [UIFont systemFontOfSize: 14.0];
    ptCell.name.adjustsFontSizeToFitWidth = YES;
    ptCell.name.minimumFontSize = 7.0;

    but neither of these have worked. By that I mean that instead of the text shrinking it truncates the tail.

    Does anyone know what I am missing? Presumably this should work because I have seen other questions complaining that it is doing that when the user does not want it to.

    • HalR
      HalR about 11 years
      Are you using an attributed string?
    • hvanbrug
      hvanbrug about 11 years
      No, I'm not. Am I supposed to be? I just tried it and notice that when I start typing beyond the field width it shrinks a little and the scrolls, but then as soon as I resign first responder it snaps back to the original font size.
    • Elijah
      Elijah almost 8 years
      Possible duplicate of UITextField minimum fontsize
  • hvanbrug
    hvanbrug about 11 years
    Are you suggesting that I subclass UITextField and catch the textdidchange notification to call this method from that, and also every time I set the text manually? Does the built in capability officially not work?
  • hvanbrug
    hvanbrug about 11 years
    ok. I already have a subclass so it makes sense to me to put it in there. I can then internally catch the change notification and resize. I am trying it right now so I guess we'll see how well it works. Unless I read your code wrong, it looks like you are checking for height instead of width.
  • hvanbrug
    hvanbrug about 11 years
    What really sucks is that the UITextField control should already be doing this automatically instead of me having to manually recreate this functionality.
  • hvanbrug
    hvanbrug about 11 years
    I was not able to use your code because it was checking the wrong dimension, but you basically confirmed that I had to do it myself rather than relying on the api to work out of the box. If you're interested see my answer below for what I ended up doing.
  • hvanbrug
    hvanbrug almost 11 years
    Yeah, I would have thought that should work too, but it didn't, neither in IB nor in code. I tend to agree that there is a bug/issue that prevents this from working through those settings.
  • Matt Powlson
    Matt Powlson almost 11 years
    This works for me: 'emailAddress = [[UITextField alloc] initWithFrame:CGRectMake(15, 117, 247, 30)]; emailAddress.delegate = self; emailAddress.textAlignment = NSTextAlignmentCenter; emailAddress.font = [UIFont fontWithName:@"Helvetica" size:21]; emailAddress.minimumFontSize = 10.0f; emailAddress.adjustsFontSizeToFitWidth = YES; emailAddress.textColor = [UIColor whiteColor]; emailAddress.backgroundColor = [UIColor clearColor]; [scrollView addSubview:emailAddress];'
  • hvanbrug
    hvanbrug almost 11 years
    I wasn't able to get that sort of thing to work. I can try again because I do not like the fact that I had to code the solution that I did. That said, I am not manually creating the textfield. It is in IB. I was just setting the parameters in code after the fact.
  • Matt Becker
    Matt Becker about 10 years
    This is the answer I was looking for. There's only one thing I'd add. I'd add fontSize = self.minimumFontSize; within the last 'if' statement. That ensures that the returned font size is never smaller than the minimumFontSize.
  • hvanbrug
    hvanbrug about 10 years
    Good catch @MattBecker. My code would have allowed it to go one point smaller than the minimum size. I will update the answer (and my own code)
  • Juraj Petrik
    Juraj Petrik about 10 years
    where is _originalFontSize coming from?
  • hvanbrug
    hvanbrug about 10 years
    It is just a private variable that I use to keep track of the full font size. As I look at it now (a year later), I think I don't need to do it this way. I could just grab the font size directly each time.
  • etayluz
    etayluz about 9 years
    It is not enough to set adjustsFontSizeToFitWidth, you must set minimumFontSize as well
  • Jason Moore
    Jason Moore over 8 years
    In Xcode 7, I just needed to to set adjustsFontSizeToFitWidth and the minimumFontSize to be smaller than the current font size. (Doesn't look great - the text jumps a bit when you type and as it gets smaller and smaller, but it does work.)
  • ToolmakerSteve
    ToolmakerSteve about 7 years
    No - you are confusing the deprecated size parameter, which is in points, with the more recent scale, which is ratio as you say. This very old thread appears to be due to a bug in storyboard, that was fixed years ago, AFAIK. What you've done is set a font size that is extremely tiny - so of course it "works" (because this bug no longer exists), BUT your answer also allows the text to become extremely tiny - unreadable. OR ELSE you didn't notice the name of the parameter you were adjusting, and set the scale to 0.5. Which indeed is a perfectly sensible solution today. ;)