skip to Main Content

I have a UITextField for which I’ve set autoAdjustFontSizeToFitWidth to true and minimumFontSize to 0. The problem is the setting shrinks the text noticeably sooner than it really should. For example, here is an image of a UITextField with the above settings:

enter image description here

The green is the background color of the UITextField. In this example, the text has not shrunk yet, but no matter what I type as the next character the text field always begins shrinking; despite clearly being enough room on the left side for a few more characters. Here is another image with additional characters entered:

enter image description here

As you can see, there is a relatively large area on the left side that the text field won’t place text in when auto adjusting. This is for a right aligned text field. The same can be said of center aligned text fields as well, where there is space on the left and right that seems as if an auto adjusting text field won’t place text inside.

How do I get it so that auto adjusting text fields use the entire available space?

2

Answers


  1. Update:

    You can do the text calculations and font resizing manually. By doing so you will avoid hacks and future compatibility issues.
    A simple implementation looks like this:

    
    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var textField: UITextField!
        
        var originalFont: UIFont!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            self.textField.font = self.textField.font?.withSize(44)
            self.textField.adjustsFontSizeToFitWidth = false
            self.originalFont = textField.font
            self.textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
    
        }
    
        @objc
        func textFieldDidChange(sender: UITextField) {
            
            let textRect = sender.textRect(forBounds: sender.bounds)
            let textWidth = textRect.width
            
            var currentFont = self.originalFont!
            var i = 0
            while (i < 10) {
                let unrestrictedTextWidth = sender.text!.boundingRect(with: CGSize(width: .greatestFiniteMagnitude,
                                                                                   height: textRect.height),
                                                                      attributes: [.font : currentFont],
                                                                      context: nil).width
                if unrestrictedTextWidth <= textWidth {
                    break
                }
                
                let factor = textWidth / max(textWidth, unrestrictedTextWidth)
                let originalSize = currentFont.pointSize
                currentFont = self.originalFont!.withSize(originalSize * factor)
                i += 1
            }
            sender.font = currentFont
        }
    }
    

    Interestingly the actual relationship between text rect and font size is non-linear and non-trivial. So I added multiple iteration steps to approximate the correct size. I chose a maximum of 10 iterations to avoid infinite loops on very small sizes and rounding errors.

    Original Answer:

    There has always been some magic around UITextField and adjustsFontSizeToFitWidth. See for example this post from 2015 about how the initial font size affects the minimum font size:
    https://stackoverflow.com/a/30881385/921573

    A UITextField with:
    Font size 17, minimum size 15 will go down to 15 if need be
    Font size 17, minimum size 10 will only go down to 14
    Font size 13, minimum size 4 will stay at 13

    In my tests, setting the minimum font size in IB to 0 just gets ignored – in order so see the shrinking effect it has to be a small value like 1.
    Setting it in code to 0 works fine.

    So I think it is safe to say that UITextField might be considered historically buggy when it comes to adjustsFontSizeToFitWidth.

    That being said, I found a workaround for you:

    class FixedTextField: UITextField {
    
        override func textRect(forBounds bounds: CGRect) -> CGRect {
    
            let magicNumber = -15.0
            
            if self.adjustsFontSizeToFitWidth {
                return CGRect(
                    x: bounds.origin.x + magicNumber,
                    y: bounds.origin.y,
                    width: bounds.size.width - magicNumber,
                    height: bounds.size.height
                )
            } else {
                return super.textRect(forBounds: bounds)
            }
        }
    
        override func editingRect(forBounds bounds: CGRect) -> CGRect {
            return self.textRect(forBounds: bounds)
        }
    }
    

    This custom text field uses countermagic to mitigate the issue.
    You may have to play with the magicNumber according to your font or dimensions or device. For me 15 works ok:

    text field fixed

    Login or Signup to reply.
  2. This works for me and the textField.textAlignment is set to .right (it will depend on how many characters you put in the textField though) :

    enter image description here

    class TextFieldOne: UITextField {
    
    
    override func alignmentRect(forFrame frame: CGRect) -> CGRect {
        
      //  let newWidth = frame.width + 10 // if you want to reduce the right side too.
        
        let x = frame.origin.x - 15 // suit yourself here
        
        let newFrame = CGRect(x: x, y: frame.origin.y, width: frame.width, height: frame.height)
        
        return newFrame
        
       
    }
    
    
    override func editingRect(forBounds bounds: CGRect) -> CGRect {
           return self.alignmentRect(forFrame: self.bounds)
        }
    
    
    override func textRect(forBounds bounds: CGRect) -> CGRect {
        return self.alignmentRect(forFrame: self.bounds)
         }
    
    
    
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search