Fix Clipping UIFont lineHeightMultiple

Setting paragraph attributes on an `NSAttributedString` can be frustrating. Without a diagram, knowing what and how to set metrics isn’t super intuitive. You’ll end up fussing with all of them until it looks about right.

The most frustrating is leading (pronounced /ˈlɛdɪŋ/ as in the metal used in printing presses). `lineHeightMultiple` works well — until your texts starts to get clipped.

var lineHeightMultiple: CGFloat
The natural line height is multiplied by this factor (if positive) before being constrained by minimum and maximum line height.

Easy enough, 1 will keep the current height of the lines, greater than 1 will increase it, and less than 1 will decrease it. Decreasing your line height with a value less than 1 decreases your top line’s height, too, of course.

The Problem

But that also means that it will inch closer and closer to the top of the view’s bounds. Eventually, if your line height is low enough (and the original font’s line height is high enough), your text will be clipped.

Buggy UITextView lineHeightMultiple?

This almost feels like a bug. That is, until you figure out what is going on — and how to correct it. The `lineHeightMultiple` isn’t shifting the top of lines, but the center. As you shrink the line height, it will inch closer and closer up.

The Solution

The easiest fix I’ve found is to shift the text’s container top inset down (as well as the entire view’s height). It feels hack-y to have to do, but it keeps all your values clean and clear. It’s rare that I get to write text layout code that isn’t littered with magic numbers.

Shifting the top down.

It’s fairly simple; you just need to shift the top down by the difference of the standard line height and the new one.

let shiftTop = ceil(font.lineHeight — paragraph.lineHeightMultiple * font.lineHeight) = shiftTop
textView.frame.size.height += shiftTop

You’ll also want to zero-out the `lineFragmentPadding` of the text view’s text container and avoid using the `textContainerInset` for other purposes.

You can download the playground here: