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

69,140

Solution 1

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

Solution 2

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

Solution 3

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


}

Solution 4

I just did that you needed , Works perfectly ! (Programmatically ONLY) - We are setting large font size to the label , in order to re-scale it to the perfect font size.

self.label.font = UIFont(name: "Heiti TC", size: 60)
self.label.numberOfLines = 0
self.label.minimumScaleFactor = 0.1
self.label.baselineAdjustment = .alignCenters
self.label.textAlignment  = .center

If you are using AutoLayout, implement this code in viewDidLayoutSubviews (after the layout has been laid out).

Yours, Roi

Solution 5

Swift 4 conversion of above:

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

        return boundingBox.height
    }

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

        return boundingBox.width
    }
}

Swift 5

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

        return boundingBox.height
    }

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

        return boundingBox.width
    }
}
Share:
69,140

Related videos on Youtube

MLyck
Author by

MLyck

Updated on July 09, 2022

Comments

  • MLyck
    MLyck almost 2 years

    I've been looking for something to dynamically adjust my fontSize to fit the width of my layout box. But all the answers I can find, is either to use this:

    label.adjustsFontSizeToFitWidth = true
    

    Which does work. But only if I don't have setTranslatesAutoresizingMaskIntoConstraints set to false.

    Please note that I don't use storyboards. So to have full control over my other constraints I need this line:

    label.setTranslatesAutoresizingMaskIntoConstraints(false)
    

    So how can I adjust the font size to fit the width without using storyboard and when I can't use adjustsFontSizeToFitWidth.

    After I figure out how to adjust the font size to fit the width. I also need to adjust the height of the layout box to fit the font size. There seems to be documentation on that though, but if you happen to know the answer of this as well, it would be greatly appreciated.

    Thanks in advance

  • MLyck
    MLyck about 9 years
    Unfortunately I can't use "label.adjustsFontSizeToFitWidth = true" cause I use programmatic constraints and thus I need the following line label.setTranslatesAutoresizingMaskIntoConstraints(false) which seems to break adjustFontSizeToFitWith. I tried both your scenarios, unfortunately without luck. Thanks for the reply though! I appreciate it.
  • Rahul K Rajan
    Rahul K Rajan over 8 years
    Please explain how it works,that would be nice if you are
  • shim
    shim over 7 years
    Response doesn't match the language of the question
  • Vinu David Jose
    Vinu David Jose over 7 years
    @shim Thanks for the comment! Edited
  • andrewcar
    andrewcar over 7 years
    I am using programmatic constraints and just setting .numberOfLines to 0 made .adjustsFontSizeToFitWidth start working again. Thanks
  • Hemang
    Hemang almost 7 years
    Why take 10 in the example?
  • Yaroslav Dukal
    Yaroslav Dukal almost 6 years
    any explanation how to use?
  • user25
    user25 about 5 years
    adjustsFontSizeToFitWidth does nothing, useless. The only function it has is reducing text if it is bigger than container's width
  • user25
    user25 about 5 years
    adjustsFontSizeToFitWidth does nothing, useless. The only function it has is reducing text if it is bigger than container's width
  • Brian Hong
    Brian Hong almost 5 years
    Because of the lack of reputation, I add a response to the comment of user25. if you want to resize the font so that it fits the content, you can do something like this in viewDidLayoutSubviews() (50.0 is the font size set in storyboard in my case.) if let tw = totalWidth, tw < safeAreaWidth { let fontSize: CGFloat = 50.0 * safeAreaWidth / tw hourLabel.font = UIFont(name: "Menlo-Bold", size: fontSize) }
  • Joseph Astrahan
    Joseph Astrahan about 4 years
    Agreed how do you use this?
  • Joseph Astrahan
    Joseph Astrahan about 4 years
    How do you use this though?
  • SeanRobinson159
    SeanRobinson159 almost 4 years
    @JosephAstrahan I updated my answer with an example. Hope that helps!