Skip to content

Instantly share code, notes, and snippets.

@joncardasis
Last active May 11, 2023 03:20
Show Gist options
  • 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
}
}
@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