Instead of fitting a layout object to contain a text label of fixed size, it is sometimes required to fit the text inside a fixed layout.
In this particular case, we iteratively change the font until it converges on an optimized fit using the instantaneous aspect ratio of the label text. This helps to ensure the label fills the available space as much as possible.
extension UIFont {
static func createFittedFont(maxWidth: CGFloat, maxHeight: CGFloat, text: String, fontName: String) -> UIFont? {
let defaultSize: CGFloat = 20
guard var font = UIFont(name: fontName, size: defaultSize) else {
print("Error: invalid font name")
return nil
}
let defaultWidth = text.width(withConstraintedHeight: CGFloat.greatestFiniteMagnitude, font: font)
let defaultHeight = text.height(withConstrainedWidth: CGFloat.greatestFiniteMagnitude, font: font)
let defaultAspectRatio = defaultHeight / defaultWidth
let targetAspectRatio = maxHeight / maxWidth
let eta: CGFloat = 0.03
let coefficient: CGFloat = 0.4
while (true) {
let delta = (targetAspectRatio < defaultAspectRatio) ?
maxHeight / text.height(
withConstrainedWidth: CGFloat.greatestFiniteMagnitude,
font: font) - 1.0
:
maxWidth / text.width(
withConstraintedHeight: font.pointSize,
font: font) - 1.0
if(fabs(delta) <= eta) { break }
let newSize = font.pointSize * (1.0 + coefficient * delta)
font = font.withSize( newSize )
}
return font
}
}```