Search code examples
macosswiftuinstableview

SwiftUI Table rowHeight on macOS


Looking for some advice trying to use Table on macOS using SwiftUI. Table was introduced in macOS 12, and I'm trying my darnedest to not step down into AppKit or replicate any existing functionality - I can't seem to find a solution to a SwiftUI version of NSTableView's rowHeight property.

There is a .tableStyle modifier but only allows for customization of insets and alternating row styling. Modifying the frame in the row views doesn't take effect, at least not the ways I've tried.

First, am I missing something obvious (or not obvious) and there is a way to do this? The underlying AppKit view is a SwiftUITableView that seems to inherit to NSTableView. I can adjust the rowHieght in the debugger, but only effects the table view's background. Second any recommendations on the way to approach this - other than using NSTableView and wrapping in a NSViewRepresentable, or manipulating the established NSView hierarchy using some SwiftUI/AppKit trickery?

Some elided code demonstrating the use of Table

struct ContentTable: View {
    var items: [ContentItem]
    
    @State var selection = Set<ContentItem.ID>()
    
    var body: some View {
        Table(selection: $selection) {
            TableColumn("Name") {
                Text($0.name)
                    .frame(height: 80) // Only way found to set height information
            }.width(min: 200, ideal: 250)

            TableColumn("Description", value: \.description)
        } rows: {
            ForEach(items) {
                TableRow($0)
            }
        }
        .tableStyle(.inset(alternatesRowBackgrounds: true))
    }
}

Here's a side-by-side of SF Symbols (Left) and the SwiftUI Table I'm using. Both are using the inset/alternating row styles. Presentation wise I'd like to give the rows more space to breathe.

side-by-side comparison


Solution

  • The way I do this is by utilizing the padding modifier in the content of declared TableColumns.

    The important part is the table will adjust the row by the lowest padding value across all columns.

    I would not try to approach this with tableStyle. There are no public configurations as of now.

    It's important to note an undeclared padding defaults to 0.

    struct ContentTable: View {
        var items: [ContentItem]
        
        @State var selection = Set<ContentItem.ID>()
        
        var body: some View {
            Table(selection: $selection) {
                TableColumn("Name") {
                    Text($0.name).padding(.vertical, 8) // <--- THIS WILL TAKE PRECEDENCE.
                }.width(min: 200, ideal: 250)
    
                TableColumn("Description") {
                    Text("\($0.description)").padding(.vertical, 16)  // <--- THIS WILL BE INEFFECTIVE.
                }
            } rows: {
                ForEach(items) {
                    TableRow($0)
                }
            }
            .tableStyle(.inset(alternatesRowBackgrounds: true))
        }
    }