Swift - Adjusting fontSize to fit the width of the layout (programmatically)

Try the following commands for your label:

label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.2

And try to change the lines of the label to 0 and 1 (check both cases):

label.numberOfLines = 0 // or 1

MinimumScaleFactor range is 0 to 1. So we are setting the MinimumScaleFactor with respect to our font size as follows

Objective C

[lb setMinimumScaleFactor:10.0/[UIFont labelFontSize]];
lb.adjustsFontSizeToFitWidth = YES;

Swift 3.0

lb.minimumScaleFactor = 10/UIFont.labelFontSize
lb.adjustsFontSizeToFitWidth = true

I don't know if this will completely answer your question, but you can use this extension to calculate your own font sizes to fit.

Swift 3

extension String {
   func height(constrainedBy width: CGFloat, with font: UIFont) -> CGFloat {
       let constraintSize = CGSize(width: width, height: .max)
       let boundingBox = self.boundingRectWithSize(constraintSize, options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

       return boundingBox.height
   }

   func width(constrainedBy height: CGFloat, with font: UIFont) -> CGFloat {
       let constrainedSize = CGSize(width: .max, height: height)
       let boundingBox = self.boundingRectWithSize(constrainedSize, options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

       return boundingBox.width
   }
}

Update - Adds example and updates code to Swift 5

Swift 5

extension String {
    func height(constrainedBy width: CGFloat, with font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)

        return boundingBox.height
    }

    func width(constrainedBy height: CGFloat, with font: UIFont) -> CGFloat {
        let constrainedRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = self.boundingRect(with: constrainedRect, options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)

        return boundingBox.width
    }
}

Here is my logic to make sure that two different labels had the same font size. In my case it was two labels for the user's first and last name. The reason I needed two labels for this, is because each label is independently animated.

class ViewController: UIViewController {
      ...

      func viewDidLoad() {
         super.viewDidLoad()
         fixFontSizes()
         ...
      }

      func fixFontSizes() {
         let leadingMargin = self.leadingContainerConstraint.constant
         let trailingMargin = self.trailingContainerConstraint.constant
         let maxLabelWidth = self.view.bounds.width - leadingMargin - trailingMargin

         let firstName = self.firstNameLabel.text ?? ""
         let lastName = self.lastNameLabel.text ?? ""

         let firstNameWidth = firstName.width(constrainedBy: self.container.bounds.height, with: self.firstNameLabel.font)
         let lastNameWidth = lastName.width(constrainedBy: self.container.bounds.height, with: self.lastNameLabel.font)

         let largestLabelWidth = max(firstNameWidth, lastNameWidth)
         guard largestLabelWidth > maxLabelWidth else { return }

         let largestTextSize = max(self.firstNameLabel.font.pointSize, self.lastNameLabel.font.pointSize)

         let labelToScreenWidthRatio = largestLabelWidth / maxLabelWidth
         let calculatedMaxTextSize = floor(largestTextSize / labelToScreenWidthRatio)

         self.firstNameLabel.font = self.firstNameLabel.font.withSize(calculatedMaxTextSize)
         self.lastNameLabel.font = self.lastNameLabel.font.withSize(calculatedMaxTextSize)
      }


}