---
title: iOS Text Kit Basics
teaser: Apple's Text Kit framework, added to iOS 7, lets you customize layout of rich
  text in your apps in a variety of ways. This will show you how to get started by
  implementing a multi-column view using Text Kit.
tags: ios
author: Jack Nutting
published_on: 2014-05-30
---

In the iOS 7 SDK, Apple gave developers programmatic access to [Text
Kit][text_kit], an advanced text layout system that lets you easily display and
edit rich text in your applications. Although these capabilities are new to iOS,
they've existed in other contexts (OS X since its first release, and OpenStep
before that) for nearly two decades, in what's called the [Cocoa text
system][cocoa_text_system_overview]. In fact, if you've used OS X at all, there
is a near 100% chance that you've run apps built with the Cocoa text system.

However, for iOS developers, who often are not steeped in the details of OS X
development, the details of using the supplied text layout system are new, and
may seem mysterious at first. I intend to help you understand the basics of how
this works, and see how you can add rich text features to your own apps.

## The simplest thing

Let's start off by making the simplest possible app that shows off some of what
Text Kit can do. In Xcode, create a new project using the *Single View
Application* template, and name it *Simple Text View*. Select *Main.storyboard*,
use the *Object Library* to find a `UITextView` object, and drag it out to fill
the view of the available view controller; you'll see blue guidelines appear and
the whole thing snap into place when it's properly centered. Then use the
*Attributes Inspector* to change the text view's *Text* attribute from *Plain*
to *Attributed*. What this does is tell the text view to allow rich text by
using an *attributed string*. An attributed string, which is represented in iOS
by the `NSAttributedString` class, is simply a string that has some attached
metadata describing its attributes. This metadata may contain any number of
ranges of characters, each with its own set of attributes. For example, you
could specify that starting at the fifth character, the next six characters are
bold, and that starting at the tenth character, the next five characters are
italicized; In that case, the tenth character would be both bold and italicized.
In effect,

  0123**45678*****9****ABCD*EF

However, plenty of rich text content is created not by programmatically
specifying ranges and attributes, but  by users working in an editor that lets
them create rich text. That's a use case that is fully supported by `UITextView`
starting in iOS 7.

![UITextView in Interface Builder](https://images.thoughtbot.com/ios-core-text/xcode-uitextview.png)

To prove this, use the Attributes Inspector to modify parts
of the "Lorem ipsum" text that the view contains by default. Use the controls in
the inspector to change some fonts, adjust paragraph alignment, set foreground
and backgroud colors, whatever you want. When you hit cmd-R to run the app in
the iOS Simulator or on a device, you'll see that all the formatting changes you
made show up on the device. You can tap to edit the text at any point, and the
formatting that applies where the cursor is will carry on to new characters you
type, just as you'd expect from any word processor application.

## The innards

So far, so good. Even better, it turns out that a few other popular UIKit
classes, namely `UILabel` and `UITextField`, also allow the use of attributed
strings in iOS 7. This means that if you just want to display some rich text in
a single rectangular box, you're all set. Just put a properly configured UILabel
where you want to show your rich text, and you're done! This simple task was
remarkably hard to accomplish before iOS 7, so right there we've made a huge
leap.

But, what if you want to do more? There are certain kinds of layout tricks that
none of the UIKit classes can do on their own, out of the box. For example, if
you want to make text flow around a graphic, or make a single string fill up one
rectangle before spilling into another (as in the case of multiple columns),
you'll have to do more. Fortunately, the innards of Text Kit, which are used by
UITextView and the rest, are at your disposal in the form of the
`NSTextStorage`, `NSLayoutManager`, and `NSTextContainer` classes. Let's talk
about these one by one:

- `NSTextStorage` is actually a subclass of `NSMutableAttributedString`, which
  itself is a subclass of `NSAttributedString`. It adds some functionality that
  is useful for dealing with a user editing text, and nothing more.
- `NSTextContainer` is an abstract description of a two-dimensional box that
  text could be rendered into. Basically, this class is little more than a
  glorified size. It contains a few parameters for describing how text should
  behave when rendered within a box of its size, and that's about it.
- `NSLayoutManager` is the real brains of the operation. It knows how to take an
  `NSTextStorage` instance, and layout all the characters it contains into the
  virtual boxes described by one or more `NSTextContainers`.

A class like `UITextView` uses these components to do all its text layout. In
fact, `UITextView` has three properties called `textStorage`, `textContainer`,
and `layoutManager` for just this purpose. When `UITextView` wants to draw its
content, it tells its `layoutManager` to figure out which glyphs (the graphical
representations of the characters it contains) from its `textStorage` can fit
within its `textContainer`, then it tells the `layoutManager` to actually draw
those glyphs at a point inside the text view's frame. So you see that the design
of `UITextView` itself is inherently limited to a single rectangle. In order to
get a feel for how these innards work, I'll now show you a `UIView` subclass
that will display rich text in multiple columns, a trick that `UITextView`
really can't pull off in its current form.

## Create TBTMultiColumnTextView

In your open Xcode project, create a new subclass of `UIView` called
`TBTMultiColumnView`. Like `UITextView`, this class will have `textStorage` and
`layoutManager` properties. Unlike `UITextView`, it will keep track of multiple
independent text containers and multiple origins for drawing rectangles. The
first thing you should do is create a class extension at the top of the file,
containing the following properties:

```objective-c
@interface TBTMultiColumnTextView ()

@property (copy, nonatomic) NSTextStorage *textStorage;
@property (strong, nonatomic) NSArray *textOrigins;
@property (strong, nonatomic) NSLayoutManager *layoutManager;

@end
```

Besides the `NSTextStorage` and `NSLayoutManager` instances, we're also going to
maintain an array of origins, each corresponding to an `NSTextContainer`. We
don't have to hang onto the text containers themselves, because the layout
manager keeps its own list, which we can access.

Now, let's get started with the methods for this class. First, override
`viewDidLoad` as shown here:

```objective-c
- (void)awakeFromNib {
    [super awakeFromNib];
    self.layoutManager = [[NSLayoutManager alloc] init];

    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"constitution"
                                             withExtension:@"rtf"];
    self.textStorage = [[NSTextStorage alloc] initWithFileURL:fileURL
                                                      options:
                        @{NSDocumentTypeDocumentAttribute:NSRTFTextDocumentType}
                                           documentAttributes:nil
                                                        error:nil];
    [self createColumns];
}
```

This method is pretty straightforward. It starts off by creating a layout
manager, which we'll use every time we need to draw this object's content. Then
we read the contents of an RTF file, which we've included in our project, into
an `NSTextStorage` instance. Our project contains an RTF file that contains the
U.S. constitution, but you can use any RTF document you have at hand. Since this
object will need to be redrawn any time the text storage changes, we implement
the setter, like this:

```objective-c
- (void)setTextStorage:(NSTextStorage *)textStorage {
    _textStorage = [[NSTextStorage alloc] initWithAttributedString:textStorage];
    [self.textStorage addLayoutManager:self.layoutManager];
    [self setNeedsDisplay];
}
```

Note that we have a special way of making a new copy of the object that's passed
in. As it turns out, just sending `copy` to an instance of `NSTextStorage`
actually returns an instance of an immutable parent class (just like you'd
expect with, say, an `NSMutableString`). That's why we take the step of
explicitly creating a new instance based on the received parameter.

At the end of `awakeFromNib`, we called the `createColumns` method, which is
where most of this class's work really happens. It looks like this:

```objective-c
- (void)createColumns {
    // Remove any existing text containers, since we will recreate them.
    for (NSUInteger i = [self.layoutManager.textContainers count]; i > 0;) {
        [self.layoutManager removeTextContainerAtIndex:--i];
    }

    // Capture some frequently-used geometry values in local variables.
    CGRect bounds = self.bounds;
    CGFloat x = bounds.origin.x;
    CGFloat y = bounds.origin.y;

    // These are effectively constants. If you want to make this class more
    // extensible, turning these into public properties would be a nice start!
    NSUInteger columnCount = 2;
    CGFloat interColumnMargin = 10;

    // Calculate sizes for building a series of text containers.
    CGFloat totalMargin = interColumnMargin * (columnCount - 1);
    CGFloat columnWidth = (bounds.size.width - totalMargin) / columnCount;
    CGSize columnSize = CGSizeMake(columnWidth, bounds.size.height);

    NSMutableArray *containers = [NSMutableArray arrayWithCapacity:columnCount];
    NSMutableArray *origins = [NSMutableArray arrayWithCapacity:columnCount];

    for (NSUInteger i = 0; i < columnCount; i++) {
        // Create a new container of the appropriate size, and add it to our array.
        NSTextContainer *container = [[NSTextContainer alloc] initWithSize:columnSize];
        [containers addObject:container];

        // Create a new origin point for the container we just added.
        NSValue *originValue = [NSValue valueWithCGPoint:CGPointMake(x, y)];
        [origins addObject:originValue];

        [self.layoutManager addTextContainer:container];
        x += columnWidth + interColumnMargin;
    }
    self.textOrigins = origins;
}
```

This method is honestly a little longer than we'd like, but for this example it
does the job. This method may need to be called multiple times, whenever the
view's coordinates are adjusted (such as when the device rotates), so we need to
make sure it can run multiple times without ending up in a weird state. So, it
starts off by removing any old text containers that may be attached to the
layout manager. It does this because the whole point of this method is to create
a fresh set of text containers, and having old ones lying around will only give
us grief. This method then calculates appropriate text container sizes depending
on the view's size and some hard-coded values for the number of columns and the
amount of margin that should appear between columns. Finally it creates and
configures a number of containers and and equal number of points (wrapped in
`NSValue` objects).

Next we're going to make use of all those containers and points we just created.
The drawRect: method tells the layout manager to finally draw its content into
each text container. It looks like this:

```objective-c
- (void)drawRect:(CGRect)rect {
    for (NSUInteger i = 0; i < [self.layoutManager.textContainers count]; i++) {
        NSTextContainer *container = self.layoutManager.textContainers[i];
        CGPoint origin = [self.textOrigins[i] CGPointValue];

        NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:container];

        [self.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:origin];
    }
}
```

All we do here is loop over all available text containers and origin points,
each time asking the layout manager which glyphs it can fit into the container,
then telling the layout manager to draw those glyphs at the origin point.

That's just about all we need. In order to make things work after device
rotation, however, we need to do one more thing. By overriding `layoutSubviews`,
which is called when the view rotates, we can make sure that the columns are
regenerated for the new size:

```objective-c
- (void)layoutSubviews {
    [super layoutSubviews];
    [self createColumns];
    [self setNeedsDisplay];
}
```

That's all we need to make this class draw rich text in two columns, and
automatically adjust for changes in view geometry. To see this in action, go
back to the storyboad and follow these steps:

- Remove the `UITextView` you added at the start.
- Find a `UIView` in the object library, drag it into the view and make it fill
  the view completely.
- Use the *Identity Inspector* to change this object's class to
  `TBDMultiColumnView`.
- To make sure the view's geometry changes along with its superview (e.g. when
  the device rotates), add constraints from the view to its superview for top,
bottom, leading, and trailing space. This is most easily accomplished by
clicking the *Pin* button at the bottom of Interface Builder's editing area, and
selecting each of the four red, dashed-line symbols surrounding the little
square (which represents the selected view). That sounds complicated, but once
you see it, you'll get it.

Once you've taken those final steps, you can build and run in the simulator or
on a device, and see your multicolumn display in all its glory!

![Multi Column Text View running on iOS Simulator][simulator-text]

[simulator-text]: https://images.thoughtbot.com/ios-core-text/simulator-multi-column-text-view.png

## Closing remarks

This class demonstrates a technique for creating a view that lets rich text flow
across multiple columns in just a few lines of code. But we're really just
scratching the surface here. Besides flowing across multiple rectangles, Text
Kit will let you do plenty of other things, including drawing text inside the
path of an arbitrary shape, making text flow around other paths, and more. You
can learn more about thse techniques by looking at Apple's [iOS Text Kit
Overview][text_kit], as well as their Mac documentation for the [Cocoa text
system][cocoa_text_system_overview], which is where much of Text Kit's
functionality originated.

[text_kit]: https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html
[cocoa_text_system_overview]: https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Introduction/Introduction.html
