TechnologiesSwiftUI

DisclosureTableRow struct

iOSmacOStvOSwatchOSvisionOS✓ renders

A kind of table row that shows or hides additional rows based on the state

How it works

DisclosureTableRow is a table row that displays a value and can expand to reveal a nested set of child rows beneath it. It brings the familiar disclosure-triangle affordance of an outline to the columnar world of Table, letting you present hierarchical data — categories and their members, a folder and its contents — without abandoning the aligned columns a table gives you. Reach for it inside a Table's rows builder whenever each top-level item owns a collection of sub-items that the reader should be able to fold away or drill into.

  1. Construct the row from a parent value

    The primary initializer takes the value the row represents and a @TableRowBuilder closure that produces the rows to show when the row is expanded. In the example, DisclosureTableRow(group) is handed each top-level Item, so the parent occupies its own row across the Name and Price columns while its children stay tucked beneath the disclosure triangle.

  2. Supply the children in the row builder

    The trailing closure is a row builder, not a view builder, so its contents must themselves be table rows. Here a nested ForEach(group.children) emits a TableRow(child) for each sub-item, and those generated rows are what the disclosure triangle collapses and reveals.

  3. Let the children flow through the same columns

    DisclosureTableRow doesn't define its own layout — the parent and the disclosed children are rendered by the table's column definitions, TableColumn("Name", value: \.name) and TableColumn("Price", value: \.price). Because every row maps the same Item properties, the nested Apple and Banana rows stay aligned under their headers just like Fruit does.

  4. Provide stable identity for the rows

    Disclosure rows participate in the same diffing as any table row, so the underlying value needs an identity. The example conforms Item to Identifiable with a UUID id, which lets the outer ForEach(items) and the inner ForEach(group.children) track each parent and child as the table expands or collapses.

Try it — Add a third entry to one item's children array, such as another Item(name: "Yogurt", price: "$1.10") under Dairy, and watch the new row appear beneath the Dairy disclosure triangle when it's expanded.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

DisclosureTableRow.swift
struct DisclosureTableRowDemo: View {
    struct Item: Identifiable {
        let id = UUID()
        let name: String
        let price: String
        var children: [Item] = []
    }

    let items = [
        Item(name: "Fruit", price: "", children: [
            Item(name: "Apple", price: "$1.20"),
            Item(name: "Banana", price: "$0.80")
        ]),
        Item(name: "Dairy", price: "", children: [
            Item(name: "Milk", price: "$2.50")
        ])
    ]

    var body: some View {
        Table(of: Item.self) {
            TableColumn("Name", value: \.name)
            TableColumn("Price", value: \.price)
        } rows: {
            ForEach(items) { group in
                DisclosureTableRow(group) {
                    ForEach(group.children) { child in
                        TableRow(child)
                    }
                }
            }
        }
        .padding()
    }
}
Live preview
Name Price
Name Price
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →