# Text Truncation in a UILabel

*When NSLineBreakByTruncatingTail and the default ellipsis doesn’t quite cut it for us, here are three methods to truncate a string to a constraining size and append custom text to the truncated string:*

- subtraction until it fits
- addition until it doesn’t
- binary search, because who doesn’t like log(N)

Let’s say you have a lot of text with variable length and only a 320x100 frame to display it. You don’t want to leave the user with a crude cut in characters without any indication that there’s more to read. What do you do?

iOS offers an easy fix:

The `UILabel`

’s’ `lineBreakMode`

property is set by default to `NSLineBreakByTruncatingTail`

, resulting in a simple but powerful `...`

to signal the end:

The ellipsis is the universal solution to this problem, and a fine one at that for signaling the intentional omission of a word. If you want to also signal that there is some way to interact with your truncated text – for example, let’s say you want to make a truncated `UILabel`

tappable for an expanded view – you might want to add a custom call to action: “see more”, “expand”, or even an ellipsis with a different color.

Here’s the rub: **as far as I can tell, there is no way to reach under the hood and override iOS truncation to append your own version of an ellipsis.** You can, however, write a decent workaround with a fit function using `NSString`

’s `[boundingRectWithSize:options:attributes:context]`

and mutate a string until it does fit. With a little research, I wrote something to the effect. First, the fit function, as a category on `NSString`

:

Using `[boundingRectWithSize:options:attributes:context:]`

, we can check if the **maximum height that a string would take up in a constraining width** is less than or equal to the **constraining height**. Our fit function takes a parameter, `trailingString`

, so that we can call the fit function on the final form of the text of the label: one part truncated string, one part custom string to indicate omission.

Now we can implement our first solution.

# Truncation Method: Subtraction

Mutate the string, subtracting characters until `[willFitToSize:trailingString:attributes:]`

returns `YES`

.

We can create a mutable copy of the string and subtract one character at a time, stopping when height of bounds fits the constraining height. We can use that index to reconstruct string that fits.

Performance is O(N), where N is the length of the string. This naive implementation isn’t so bad if you know that your strings are limited to a certain character length, but if your string is a million characters and your frame fits 100, you might want to look at the next implementation.

# Truncation Method: Addition

We add one character at a time from the string and stop when height of bounds exceeds the constraining height. We use the index to reconstruct string that fits.

Performance: constant in the size parameter.

While this implementation should be sufficient for most cases (and the most efficient, for super long strings), I was excited to try one more implementation: binary search. I’ve known in theory that binary search is a useful algorithm for sorting problems, but I’ve never encountered the fabled Algorithm in the wild.

This problem presented a rare opportunity to implement an algorithm outside the laboratory of computer science assignments.

# Truncation Mode: Binary Search

Using 0 and N as starting indices, where N is the length of the string, we perform a binary search that maintains the invariants that:

- height at minIndex <= size.height
- height at maxIndex > size.height

We return minIndex when minIndex and maxIndex are adjacent. Performance: log(N).

Hopefully, this saves you some heartache the next time you want to add a fancy “more” indicator. You can check out how the truncation works here.