What to do when .allowsTightening and .minimumScaleFactor are not cutting it

Dealing with Word Breaks in SwiftUI Text

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.

iOS 14 Cocktail WidgetThis 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.

AardvarkBananaI 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.

Using .allowsTightening is a good idea, and setting a .minimumScaleFactor is important.

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

Note: If you are a SwiftUI developer, you need to get Core Data Mastery from Big Mountain Studio. You'll learn everything you need to take your apps to the next level.

Post the first comment:

I'll never share your email address and it won't be published.

What Is This?

davidgagne.net is the personal weblog of me, David Vincent Gagne. I've been publishing here since 1999, which makes this one of the oldest continuously-updated websites on the Internet.

bartender.live

A few years ago I was trying to determine what cocktails I could make with the alcohol I had at home. I searched the App Store but couldn't find an app that would let me do that, so I built one.

Hemingway

You can read dozens of essays and articles and find hundreds of links to other sites with stories and information about Ernest Hemingway in The Hemingway Collection.