Hide and show SwiftUI `Table` columns

775 views Asked by At

I am using SwiftUI's Table to generate a table for a macOS app. I would like to be able to hide columns based on AppStorage entries. TableColumnBuilder doesn't appear to support conditional statements:


Table(tableData) {
    // This does not work:
    if showColumndA {
        TableColumn("Column A", value: \Row.columnA)
    }
    // Other columns genereated here
    // ...
}

Is there a way to build a table with SwiftUI where the user can choose what columns to show?

(Showing different tables based on all possible permutations for what columns are visible does not work for my situation)

1

There are 1 answers

2
DudeOnRock On

As mentioned by Geoff Hackworth there appears to be a solution for macOS 14 (which was not an option for me at the time of this writing).

I did end up solving my problem by extending @TableColumnBuilder to support conditionals (thanks Swift Lee for your amazing writeup of the @resultBuilder API):

extension TableColumnBuilder {
    static func buildEither<Column>(first column: Column) -> Column
        where
            RowValue == Column.TableRowValue,
            Sort == Column.TableColumnSortComparator,
            Column : TableColumnContent
    {
        column
    }

    static func buildEither<Column>(second column: Column) -> Column
        where
            RowValue == Column.TableRowValue,
            Sort == Column.TableColumnSortComparator,
            Column : TableColumnContent
    {
        column
    }
}

The following little example app uses my @TableColumnBuilder extension to randomly show either Column A or Column B:

struct Row: Identifiable {
    let id: UUID
    var columnA: String
    var columnB: String
    var columnC: String

    init(columnA: String, columnB: String, columnC: String) {
        self.id = .init()
        self.columnA = columnA
        self.columnB = columnB
        self.columnC = columnC
    }
}

struct ContentView: View {

    @State
    var showColumn: Bool = Bool.random()

    @State
    var rows: [Row] = [
        .init(columnA: "1a", columnB: "1b", columnC: "1c"),
        .init(columnA: "2a", columnB: "2b", columnC: "2c"),
        .init(columnA: "3a", columnB: "3b", columnC: "3c")
    ]

    var body: some View {
        Table(rows) {
            if showColumn {
                TableColumn("A", value: \.columnA)
            } else {
                TableColumn("B", value: \.columnB)
            }
            TableColumn("C", value: \.columnC)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}