There are three ways to draw text programmatically in Cocoa: using methods of
NSAttributedString, using those of
NSCell, and using
NSLayoutManager is the most efficient.
Using the String-Drawing Convenience Methods
NSString class has two convenience methods for drawing string objects directly in an
drawInRect:withAttributes:. For strings that have multiple attributes associated with ranges and individual characters, you must use
NSAttributedString. You can draw a string (in a focused
NSView) with either the
drawInRect: method. These methods are designed for drawing small amounts of text or text that must be drawn rarely. They create and dispose of various supporting text objects, including
NSLayoutManager, every time you call them.
For repeated drawing of text, however, the string drawing convenience methods are not efficient because they do a lot of work behind the scenes. For example, to draw Unicode text, you must first convert the characters into glyphs, the elements of a font. Glyph generation is complicated because several characters may produce a single glyph and vice versa, depending on the context and other factors. In addition, the system does a lot of work setting up for glyph conversion, and the string drawing convenience methods do this work each time they draw a string. Using the layout manager directly provides significant performance improvements because it caches glyph layout and size information.
Drawing Text With NSCell
NSCell class also provides primitives for displaying and editing text.
NSCell text drawing methods are used by
NSTableView. Text drawing by
NSCell is more efficient than using the string convenience methods because it caches some information, such as the size of the text rectangle. So, for displaying the same text repeatedly,
NSCell works well, but for the most efficient display of an arbitrary text string, use
Drawing Text With NSLayoutManager
If you use the
NSTextView class to display text, either by dragging a text view object from the Interface Builder Data palette or creating a text view programmatically using the
initWithFrame: method, Cocoa automatically creates an
NSLayoutManager instance to draw your text. If you create a text view using the
initWithFrame:textContainer: method, however, or if you need to draw text directly into a different type of
NSView object, you must create the
NSLayoutManager to draw a text string directly into a view, you must create and initialize the three basic non-view components of the text system. First create an
NSTextStorage object to hold the string. Then create an
NSTextContainer object to describe the geometric area for the text. Then create the
NSLayoutManager object and hook the three objects together by adding the layout manager to the text storage object and adding the text container to the layout manager. The code in Listing 1, which could reside in the view’s
initWithFrame: method, illustrates this procedure.
Listing 1 Creating and configuring the non-view text objects
NSTextStorage *textStorage = [[NSTextStorage alloc]
initWithString:@"This is the text string."];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
You can release the text container because the layout manager retains it, and you can release the layout manager because the text storage object retains it.
To draw glyphs directly in a view, you can use the
drawGlyphsForGlyphRange: in the view’s
drawRect: method. However, you must first convert the character range you want to draw into a glyph range. If you need to select a subrange of the text in the text storage object, you can use the
glyphRangeForCharacterRange:actualCharacterRange: method. If you want to draw the entire string in the text storage object, use the
glyphRangeForTextContainer: method as in Listing 2 (which uses the
layoutManager variable name from Listing 1).
Listing 2 Drawing glyphs directly in a view
NSRange glyphRange = [layoutManager
[layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: rect.origin];
String Drawing and Typesetter Behaviors
There are differences among Cocoa’s three ways to draw text with regard to typesetter behavior, which is described in Typesetter Behaviors and Versions. By default, the string-drawing convenience methods and
NSCell objects supplied by the Application Kit use
NSLayoutManager objects use
NSTypesetterLatestBehavior. It is important to use the same typesetter behavior when both measuring and rendering text, to avoid differences in paragraph spacing, line spacing, and head indent handling.
In cases where you must measure text one way and render it another, set the typesetter behavior to match using the
setTypesetterBehavior: method defined by
NSTypesetter. For example, if you need to use an
NSLayoutManager object to measure text and convenience string drawing methods to draw it, change the layout manager’s typesetter behavior to