NSTextTable in Swift


Greetings, traveler!

With the iOS 27 SDK, Apple brought NSTextTable, NSTextTableBlock, and related TextKit APIs to UIKit. These classes have existed in AppKit for years, but iOS developers can now use them directly to build real tables inside NSAttributedString.

This is not another table view. Instead, it extends the rich text model, making tables part of the attributed text itself.

What is NSTextTable?

NSTextTable represents an entire table inside an attributed string. Instead of managing rows and cells as views, it describes the table’s layout and formatting.

A table defines properties such as:

  • number of columns
  • layout algorithm (.automatic or .fixed)
  • collapsed borders
  • empty cell behavior
let table = NSTextTable()
table.numberOfColumns = 2
table.layoutAlgorithm = .automatic
table.collapsesBorders = true

Unlike UITableView or UICollectionView, the table itself never contains cells. Every cell is represented separately by an NSTextTableBlock.

How cells work

Each cell is described by an NSTextTableBlock.

let cell = NSTextTableBlock(
    table: table,
    startingRow: 0,
    rowSpan: 1,
    startingColumn: 0,
    columnSpan: 2
)

The block stores:

  • its table
  • row and column position
  • row span
  • column span

If you’ve worked with HTML tables, rowSpan and columnSpan behave exactly as you would expect.

Tables are built through paragraph styles

One unusual aspect of this API is that cells belong to paragraphs, not directly to the table.

A paragraph style references one or more text blocks:

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.textBlocks = [cell]

That paragraph style is then applied to a portion of an attributed string.

NSAttributedString
    ↓
Paragraph
    ↓
NSParagraphStyle
    ↓
textBlocks
    ↓
NSTextTableBlock
    ↓
NSTextTable

TextKit uses this information during layout and renders the paragraph inside the corresponding table cell.

Cell appearance

Since NSTextTableBlock inherits from NSTextBlock, every cell can define its own layout and appearance.

You can configure:

  • padding
  • margins
  • borders
  • background color
  • minimum and maximum dimensions
  • percentage or absolute sizes

This makes the API much more flexible than manually aligning text with tabs or spaces.

Automatic vs fixed layout

NSTextTable supports two layout algorithms.

automatic sizes columns according to their content and the available width.

fixed relies on the sizes you specify for the table and its cells, producing a more predictable layout.

Choose the automatic algorithm for documents with varying content, and fixed when column widths should remain consistent.

Where this API makes sense

NSTextTable is designed for rich text, not interactive interfaces.

Typical use cases include:

  • document editors
  • receipts and invoices
  • reports
  • email composition
  • RTF or rich text viewers
  • exporting formatted documents
  • sharing attributed content between Apple platforms

Because the table becomes part of the attributed string, it can be copied, edited, and exported together with the surrounding text.

Where you should not use it

This API is not intended to replace UITableView or UICollectionView.

It is a poor fit for:

  • large datasets
  • scrolling grids
  • editable spreadsheets
  • reusable cells
  • lazy loading
  • pagination
  • highly interactive interfaces

Those scenarios still belong to collection and table views.

SwiftUI support

There is currently no native SwiftUI equivalent. Although Text supports AttributedString, it does not fully render every TextKit feature, including paragraph text blocks.

For now, the recommended approach is to display the attributed string inside a UITextView wrapped with UIViewRepresentable.

Final thoughts

NSTextTable extends the text system. If your application works with rich text, reports, invoices, or document editing, this API finally gives UIKit a proper way to represent real tables inside attributed strings without relying on custom layouts or manually aligned text.