There are at least a half-dozen different ways to deal with font scaling issues in a SwiftUI Text view. It took me quite some time to determine that none of them would cover all of the different scenarios presented by attempting to display variable-length cocktail titles in a small iOS 14 widget for my bartender recipe app.
This only mattered for me when working with long titles in the .systemSmall size widget. The .systemMedium and .systemLarge widgets both handle word wrapping and scaling properly as far as I’m concerned. And the small widget handles multi-word titles with no problem. But the .systemSmall widget was either truncating (which I didn’t want), word-wrapping in a weird space, or scaling the font so tiny as to be illegible.
I don’t want to truncate the text in the small widget, because then it looks like a mistake, so I couldn’t do that. If what I need to display is Purple Monkey Dishwasher there’s no problem, because SwiftUI word wraps Dishwasher to the next line. Setting the .minimumScaleFactor works, but when you actually hit the minimum, you don’t want the text to truncate a single word, which was what was happening. If the text included a single word that was too long — like AardvarkBanana — the word would get displayed in the small widget as AardvarkBana with just na on the next line, which looked idiotic.
So what I wanted was a way to allow for word wrapping for something like Purple Monkey Dishwasher but disallow word wrapping for something like AardvarkBanana.
My solution was to add a function to my struct that will tell my widget entry whether the text is one (potentially really long) word — which I don’t want to break at all — or whether it is more than one word, in which case it’s fine to break it. Then I use the result of that function in the .lineLimit() attribute of my Text view.
struct MyThing { let title: String … func correctLineLimit() -> Int { let wordcount = title.split(separator: " ") return wordcount.count > 1 ? 2 : 1 } }
if widgetFamily == .systemSmall { Text(entry.thing.title) .font(.title2) .bold() .lineLimit(entry.thing.correctLineLimit()) .allowsTightening(true) .minimumScaleFactor(0.75) }
* header image from the excellent AppCoda book Mastering SwiftUI