When you display text in your app, you might come across situations where the text layout produces undesirable results under certain layout constraints. The text could wrap on smaller devices or be truncated in certain localizations. At this point, we are well-equipped with adaptive APIs to make our layouts work on all screen sizes — for example, we have Dynamic Type, Auto Layout, and UITraitCollection.

However, some situations require fine tuning the formatting and layout of text using TextKit. Text layout APIs on Apple Platforms are provided by TextKit, which is available as part of UIKit and AppKit. TextKit is what powers UITextView and NSTextView.

One scenario where adaptivity APIs will not help is when you layout a body of text that results in an orphan word on the final line. Often, this simply looks bad. The text does not look balanced. Orphaned words are not aesthetically pleasing. This is especially true if you have only two lines of text, where the second line contains a single word. It is even worse if the text is center-aligned. How can we solve this issue?

Image our text view is laying out text like this:

TextKit manages text storage and performs layout of text-based content on iOS and
macOS.

The TextKit APIs we need to use are part of NSParagraphStyle. The first API you might try to use is NSLineBreakMode which has been available since iOS 6 and macOS 10. The .lineBreakMode of an NSParagraphStyle specifies what happens when a line is too long for a container. Your options are .byWordWrapping, .byCharWrapping, .byClipping, and more. However, none of these will address the issue of orphan words.

let textView = UITextView()
var text = AttributedString("TextKit manages text storage and performs layout of text-based content on iOS and macOS.")

// Does Not Work!
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping

text.paragraphStyle = paragraphStyle
textView.attributedText = NSAttributedString(text)

The NSParagraphStyle API we need to use is the similarly named, but much newer LineBreakStrategy, which was added in iOS 14 and macOS 11. This property specifies how the text system breaks lines while laying out paragraphs. The option we want is .pushOut, which “pushes out individual lines to avoid an orphan word on the last line of the paragraph.”

let textView = UITextView()
var text = AttributedString("TextKit manages text storage and performs layout of text-based content on iOS and macOS.")

// Fixed!
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakStrategy = .pushOut

text.paragraphStyle = paragraphStyle
textView.attributedText = NSAttributedString(text)

This will adjust the layout so that there are at least two words on the second line:

TextKit manages text storage and performs layout of text-based content on iOS
and macOS.