Search code examples
iosuikituitextfielduilabel

adjustsFontSizeToFitWidth has different effect on UILabel than on UITextField


I have an UILabel (named label) and an UITextField (named textField) set up such that they do have the same frame size and both do have adjustsFontSizeToFitWidth enabled. In addition to that I set minimumFontSize for textField and a large initial font size for label and textField. This should - according to what I read - lead to "auto-shrink" the font size of the contained text. So far so good.

As a matter of fact this leads to shrinking of the text for both label and textField, but to my surprise with different results. While the label font seams to be shrunk to fit the label bounding rectangle, the textField font is shrunk to fit the textField's width, but not its height. Here is a picture of the output (label on top, textField on bottom):

textField label difference

The documentation of UITextField.adjustsFontSizeToFitWidth explicitly states:

Normally, the text field’s content is drawn with the font you specify in the font property. If this property is set to true, however, and the contents in the text property exceed the text field’s bounding rectangle, the receiver starts reducing the font size until the string fits or the minimum font size is reached. The text is shrunk along the baseline.

This leaves me confused about the shrinking behavior of textField. Any ideas why this is happening? How do I achieve the same result for textField as I do for label?

Here is the complete playground code to generate the above image (I used autolayout, but the same thing happens when I use fixed frames):

import Foundation
import UIKit
import PlaygroundSupport

// setup liveView
var viewSize = CGSize(width: 400.0, height: 600.0)
let liveView = UIView(frame: CGRect(origin: CGPoint.zero, size: viewSize))
liveView.backgroundColor = UIColor.green
let topView = UIView()
topView.translatesAutoresizingMaskIntoConstraints = false
liveView.addSubview(topView)
topView.topAnchor.constraint(equalTo: liveView.topAnchor).isActive = true
topView.leftAnchor.constraint(equalTo: liveView.leftAnchor).isActive = true
topView.rightAnchor.constraint(equalTo: liveView.rightAnchor).isActive = true
topView.heightAnchor.constraint(equalTo: liveView.heightAnchor, multiplier: 0.5).isActive = true
let bottomView = UIView()
bottomView.translatesAutoresizingMaskIntoConstraints = false
liveView.addSubview(bottomView)
bottomView.bottomAnchor.constraint(equalTo: liveView.bottomAnchor).isActive = true
bottomView.leftAnchor.constraint(equalTo: liveView.leftAnchor).isActive = true
bottomView.rightAnchor.constraint(equalTo: liveView.rightAnchor).isActive = true
bottomView.heightAnchor.constraint(equalTo: liveView.heightAnchor, multiplier: 0.5).isActive = true

PlaygroundPage.current.liveView = liveView

// setup label
let labelSize = 0.5 * CGSize(width: liveView.frame.width, height: liveView.frame.height)
let label = UILabel(frame: CGRect(origin: CGPoint(x: 0.5 * labelSize.width, y: 0.5 * labelSize.height), size: labelSize))
label.numberOfLines = 1
label.text = "0"
label.adjustsFontSizeToFitWidth = true
label.font = UIFont(name: "Helvetica", size: 1000.0)
label.backgroundColor = UIColor.blue
label.translatesAutoresizingMaskIntoConstraints = false
topView.addSubview(label)
label.centerXAnchor.constraint(equalTo: topView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: topView.centerYAnchor).isActive = true
label.widthAnchor.constraint(equalTo: topView.widthAnchor, multiplier: 0.5).isActive = true
label.heightAnchor.constraint(equalTo: topView.heightAnchor, multiplier: 0.5).isActive = true

// setup textField
let textField = UITextField(frame: CGRect(origin: CGPoint(x: 0.5 * labelSize.width, y: 0.5 * labelSize.height), size: labelSize))
textField.text = "0"
textField.adjustsFontSizeToFitWidth = true
textField.minimumFontSize = 10.0
textField.font = UIFont(name: "Helvetica", size: 1000.0)
textField.backgroundColor = UIColor.blue
textField.translatesAutoresizingMaskIntoConstraints = false
bottomView.addSubview(textField)
textField.centerXAnchor.constraint(equalTo: bottomView.centerXAnchor).isActive = true
textField.centerYAnchor.constraint(equalTo: bottomView.centerYAnchor).isActive = true
textField.widthAnchor.constraint(equalTo: bottomView.widthAnchor, multiplier: 0.5).isActive = true
textField.heightAnchor.constraint(equalTo: bottomView.heightAnchor, multiplier: 0.5).isActive = true

Solution

  • I think it's fair to say that .adjustsFontSizeToFitWidth = true only "mostly" works.

    It is also primarily for use with string width.

    Also, UILabel and UITextField do not use the same font rendering, and do not have the same bounding-box (text fields have an inset).

    If you want both elements to have the same visual behaviors, your best bet is to use a UITextField with user interaction disabled instead of a UILabel.