Skip to content

Instantly share code, notes, and snippets.

@joncardasis
Last active May 11, 2023 03:20
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save joncardasis/4d40e9782ca6b1f3d2ef8abd0290794c to your computer and use it in GitHub Desktop.
Save joncardasis/4d40e9782ca6b1f3d2ef8abd0290794c to your computer and use it in GitHub Desktop.
Swift Dynamic Font Size for Bounds
extension UIFont {
/**
Will return the best font conforming to the descriptor which will fit in the provided bounds.
*/
static func bestFittingFontSize(for text: String, in bounds: CGRect, fontDescriptor: UIFontDescriptor, additionalAttributes: [NSAttributedStringKey: Any]? = nil) -> CGFloat {
let constrainingDimension = min(bounds.width, bounds.height)
let properBounds = CGRect(origin: .zero, size: bounds.size)
var attributes = additionalAttributes ?? [:]
let infiniteBounds = CGSize(width: CGFloat.infinity, height: CGFloat.infinity)
var bestFontSize: CGFloat = constrainingDimension
for fontSize in stride(from: bestFontSize, through: 0, by: -1) {
let newFont = UIFont(descriptor: fontDescriptor, size: fontSize)
attributes[.font] = newFont
let currentFrame = text.boundingRect(with: infiniteBounds, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: attributes, context: nil)
if properBounds.contains(currentFrame) {
bestFontSize = fontSize
break
}
}
return bestFontSize
}
static func bestFittingFont(for text: String, in bounds: CGRect, fontDescriptor: UIFontDescriptor, additionalAttributes: [NSAttributedStringKey: Any]? = nil) -> UIFont {
let bestSize = bestFittingFontSize(for: text, in: bounds, fontDescriptor: fontDescriptor, additionalAttributes: additionalAttributes)
return UIFont(descriptor: fontDescriptor, size: bestSize)
}
}
extension UILabel {
/// Will auto resize the contained text to a font size which fits the frames bounds.
/// Uses the pre-set font to dynamically determine the proper sizing
func fitTextToBounds() {
guard let text = text, let currentFont = font else { return }
let bestFittingFont = UIFont.bestFittingFont(for: text, in: bounds, fontDescriptor: currentFont.fontDescriptor, additionalAttributes: basicStringAttributes)
font = bestFittingFont
}
private var basicStringAttributes: [NSAttributedStringKey: Any] {
var attribs = [NSAttributedStringKey: Any]()
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = self.textAlignment
paragraphStyle.lineBreakMode = self.lineBreakMode
attribs[.paragraphStyle] = paragraphStyle
return attribs
}
}
@eitanaviv
Copy link

Hi there!
Thanks for that awesome code!! But, it won't work.
It does reduces the font size to be fit (as it seems to be doing), but the text is still trimmed.
Made an empty ViewController and put a label with top, left and right constraints of 24 to the superview (viewcontroller itself).
Using latest xcode and swift.
Thanks :)

@joncardasis
Copy link
Author

@eitanaviv - I agree, some instances it can be trimmed. I just made a small modification which should address this. Having centered text in a label, or depending on how the string looks, sometimes the string could be on two lines and return a false positive for its sizing. Should be better now!

@derekschinke
Copy link

NSAttributedStringKey is now NSAttributedString.Key

@Dakenti
Copy link

Dakenti commented Apr 20, 2019

hi, this does not work, i am getting dots, in one line text

@seyoung-hyun
Copy link

@joncardasis
Thanks your code :)
Can I use your code in my commercial app?
Please let me know how I can use the code for commercial distribution.

@Coder-ACJHP
Copy link

Thank you for sharing, it worked with me in iOS 11 & 14, Xcode 12

@AashirMubeeb
Copy link

AashirMubeeb commented Jun 3, 2022

Thank you
Screenshot 2022-06-03 at 11 50 38 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment