Using Text Tables
The Cocoa text system supports text tables in OS X version 10.4 and later. The main classes involved are
NSTextTable, which represents a table,
NSTextTableBlock, which represents a block of text appearing as a cell in the table, and its superclass,
NSTextBlock. This article explains how to add table support to your application.
Adding the Text Table Panel
NSTextView has built-in support for text tables, which provides the easiest way to add table support to your text view. This table support is in the form of the action method
orderFrontTablePanel:. This method inserts a table into the text view and opens a modeless utility window that floats over the application windows. This table panel enables the user to manipulate attributes of a table while the cursor or selection is in the table. The table panel is shown in Text table panel.
The user can change other aspects of the table, such as cell size and contents, by direct manipulation with the cursor.
To make the text table panel available in your text view, use Interface Builder to add the
orderFrontTablePanel: action method to your first responder and connect it to a menu item, as shown in Connecting the action method.
NSTextView defines similar action methods for opening list, link, and paragraph spacing panels.
Supporting Text Tables Programmatically
If you don’t want to use the text table panel, you can support tables programmatically by using
NSTextTable and related classes directly. The basic class in this group is
NSTextBlock, which represents a block of text laid out in a subregion of a text container. When working with tables, you use its subclass,
NSTextTableBlock, which represents a text block that appears as a cell in a table. The table itself is represented by a separate class,
NSTextTable. All of the
NSTextTableBlock objects, representing the cells in the table, refer to the
NSTextTable object, which controls their size and positioning.
Text blocks appear as attributes on paragraphs, as part of the paragraph style. An
NSParagraphStyle object can have an array of text blocks representing the table cells that contain the paragraph. The paragraph style uses an array because table cells can be nested, and the text blocks are ordered in the array from outermost to innermost. For example, if block1 contains four paragraphs, and the middle two are also in inner block2, then the text blocks array for first and fourth paragraphs is (block1) which the array for the second and third paragraphs is (block1, block2).
You add text blocks to a paragraph style object using the
setTextBlocks:, and the
textBlocks returns the array.
To implement a text table programmatically, use the following sequence of steps:
Create an attributed string for the table.
Create the table object, setting the number of columns.
Create the text table block for the first cell of the row, referring to the table object.
Set the attributes for the text block.
Create a paragraph style object for the cell, setting the text block as an attribute (along with any other paragraph attributes, such as alignment).
Create an attributed string for the cell, adding the paragraph style as an attribute. The cell string must end with a paragraph marker, such as a newline character.
Append the cell string to the table string.
Repeat steps 3–7 for each cell in the table.
The methods shown in Table creation method and Table cell creation method perform the steps from the preceding list. (All of the example methods in this article are defined in the
NSDocument subclass of a document-based application, but they could as easily belong to another object, such as a text view.) Table cell creation method performs steps 3–6 for each cell in the table, using fat borders and contrasting colors for illustrative purposes.
Listing 1 Table creation method
- (NSMutableAttributedString *) tableAttributedString
// tableString is an ivar declared in the header file as NSMutableAttributedString *tableString;
tableString = [[NSMutableAttributedString alloc] initWithString:@"\n\n"];
NSTextTable *table = [[NSTextTable alloc] init];
[tableString appendAttributedString:[self tableCellAttributedStringWithString:@"Cell1\n"
[tableString appendAttributedString:[self tableCellAttributedStringWithString:@"Cell2\n"
[tableString appendAttributedString:[self tableCellAttributedStringWithString:@"Cell3\n"
[tableString appendAttributedString:[self tableCellAttributedStringWithString:@"Cell4\n"
return [tableString autorelease];
Listing 2 Table cell creation method
- (NSMutableAttributedString *) tableCellAttributedStringWithString:(NSString *)string
NSTextTableBlock *block = [[NSTextTableBlock alloc]
[block setWidth:4.0 type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockBorder];
[block setWidth:6.0 type:NSTextBlockAbsoluteValueType forLayer:NSTextBlockPadding];
NSMutableParagraphStyle *paragraphStyle =
[[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[paragraphStyle setTextBlocks:[NSArray arrayWithObjects:block, nil]];
NSMutableAttributedString *cellString =
[[NSMutableAttributedString alloc] initWithString:string];
range:NSMakeRange(0, [cellString length])];
return [cellString autorelease];
The code in Table creation method and Table cell creation method produces the table shown in Table output.
To insert the table in text displayed in a text view, implement an action method such as the one shown in Listing 3. This method inserts the table string constructed in Table creation method in the text view, replacing the current selection (or at the insertion point, if there's no selection), and ensures that the proper notifications and delegate messages are sent.
Listing 3 Table insertion action method
- (void) insertMyTable:(id)sender
NSRange charRange = [myTextView rangeForUserTextChange];
NSTextStorage *myTextStorage = [myTextView textStorage];
if ([myTextView isEditable] && charRange.location != NSNotFound)
NSMutableAttributedString *attrStringToInsert = [self tableAttributedString];
if ([myTextView shouldChangeTextInRange:charRange replacementString:nil])
[myTextView setSelectedRange:NSMakeRange(charRange.location, 0)
NSAttributedString has the following convenience methods you can use to determine the range in the string that is covered by a text block or table:
If the given location is not in the specified block or table, these methods return a range of
(NSNotFound, 0) .
The Text Table Model
The Cocoa text table model is derived primarily from the table model defined by HTML and CSS, in which tables are built up from rows of cells. For a description of the CSS table model, refer to the following URL:
This affinity provides another way to create tables in text. You can define a table in HTML and use that data to initialize an attributed string. The attributes of that string then define the table for rendering by the Cocoa text system. You can use the following
NSAttributedString initialization methods for this purpose:
Controlling Text Block Appearance
The position of a text block is determined by its text container or containing block. In the case of a text table block, which represents a cell in a table, size and position are controlled by the text table and the block’s relation to other blocks in the table. When you initialize an
NSTextTableBlock object, you specify its row and column position as a cell within its table, and you also specify whether it spans multiple rows or columns. The
NSTextTableBlock initialization method is:
Table cell creation method shows this method in use.
In addition, you can specify the value of a number of dimensions for each block, either as an absolute value or as a percentage of the containing block. These dimensions include the following:
Padding width for each of the four sides. Padding is space surrounding the content area of the block, extending to the border.
Border width for each of the four sides. A border is space between the padding and the margin, typically colored to present a visible boundary.
Margin width for each of the four sides. The margin is space surrounding the border.
The default value for each of these dimensions is
0 , indicating no padding, border, or margin, and the natural width and height. Natural width and height of a single text block extend to the width and height of its containing block (or text container); natural width and height of multiple blocks divide the space of their containing block evenly.
The following methods specify or return values associated with these dimensions:
In these methods, the value type refers to absolute or percentage values. The dimension refers to minimum, maximum, and full width and height of the block. The layer refers to padding, border, and margin. These parameters are specified by constants described in
NSTextBlock provides the following methods to specify and return the background and border color of the block:
By default the color values are
nil , meaning no color. Note that a border with no color is invisible.
Table Layout Process
During text layout, the typesetter works with
NSTextBlock to determine the layout rectangles for the text block. If the text block is an instance of
NSTextTableBlock, it calls its containing
NSTextTable instance to perform the calculations. The typesetter stores the results of these calculations in its layout manager. There are methods in
NSLayoutManager specific to this layout process that you can use if you need to intervene in the process.
To begin the text block layout process, the typesetter proposes a large rectangle within which the text block should fit. For the outermost block, this is determined by the text container; for inner blocks, it is determined by the containing block. The block object then decides what area within the proposed rectangle it should actually occupy.
The text block actually determines two rectangles: first, the layout rectangle, within which the text in the block is to be laid out; second, the bounds rectangle, which contains additional space for padding, borders, border decoration, and margins. The text block calculates the layout rectangle immediately before the typesetter lays out the first glyph because it is needed for all subsequent layout of text in the block. The layout rectangle is often quite tall because, at this point, the height of the text to be laid out has not yet been determined. The text block calculates the bounds rectangle immediately after the last glyph in the block has been laid out, and it is based on the actual rectangle used for the text within the block. Under some circumstances, the bounds rectangle may be adjusted subsequently as additional blocks in the same table are laid out.
To find the layout and bounds rectangles, the typesetter calls the following
NSTextTableBlock object, in turn, calls its
NSTextTable object to perform these calculations, using the following methods:
The typesetter stores the results of these methods in the layout manager using the following methods:
The typesetter uses the following
NSLayoutManager methods when it needs to determine the space used by previously laid text blocks:
The preceding methods cause glyph generation but do not force layout. This avoids an infinite recursion when the methods are called during layout. For the same reason, the following variants of existing
NSLayoutManager methods have an option to prevent them from causing layout:
If no rectangle has been set, the preceding methods return
At display time the text is drawn as usual, as described in “The Layout Manager,” except that the text block draws background and border decoration while glyph backgrounds are being drawn, using the following method:
If the text block is an
NSTextTableBlock object, it calls its text table for this purpose, using the following