Dynamic & Efficient Font Resizing on iOS

AccessibilityKit is used in the award-winning, low-vision keyboard FlickType.

AKTextView in action

Why?

Writing an iOS app for people who are visually impaired, I needed to display some text on the screen in the most accessible way possible. Besides using voice announcements for users who are completely blind, and using a high contrast scheme for users with low vision, I also needed to use the largest possible font size for any given text. With Apple having a great history on all things typography, I was sure there was a way to do this with UIKit, and so I started looking.

Isn’t this already supported?

While UILabel has an adjustsFontSizeToFitWidth property, it only works if text is rendered in a single line, regardless of how long it is. Its more sophisticated cousin class,UITextView, does not have that property to begin with. There are many ways to get the rectangle of a rendered string for a specified font size, such as sizeThatFits(), but you can’t easily go the other way around and get a font size to fit a given rectangle. A few Google searches show many people have been asking for this, and there are multiple proposed solutions which are either painfully slow, or don’t always work, or both! 😩

The solution 😎

The scientist in me wanted to find an analytical solution to the problem, some nice set of formulas that would give us a nice approximation to the desired font size. But sizing text is no easy task. In fact, sizing characters, words and paragraphs is a complex enough process that is almost as hard as actually drawing the text — thankfully drawing text on iOS if quite fast. So we have the tool we need to perform a search for our target font size, and if we are clever about it we could do this in just a few steps.

The approach I settled on is using a binary search to quickly home into the best approximation of the ideal font size, within a specified margin of error. In this instance, error is defined as getting a slightly smaller size than the ideal fit, meaning the text will never touch or cross the view bounds.

After a lot of trial and error, I was quite happy with the results. I decided to put everything together inside a highly reusable and performantUIView subclass, AKTextView:

AccessibilityKit’s AKTextView is a text view that will automatically and efficiently draw text using the largest possible font size, spanning multiple lines as needed to fill the current view’s bounds. It’s great for when you need some text or label to be as visually accessible as possible, or to just generally make it more appealing. It’s efficient enough to even do real-time resizing.
Realtime resizing with multiple attributes and emoji

As a bonus, you can also use all attributes of NSAttributedString, as well as a built-in vertical alignment option. And emoji, of course. Go wild!

Usage 🚀

let view = AKTextView()
// Use a simple or fancy NSAttributedString
view.attributedText = .init(string: "Some text here")

That’s it 👏

Let me know what you think in the comments. Swift source code is on GitHub!