Skip to content

Instantly share code, notes, and snippets.

@vuon9
Created June 29, 2025 07:03
Show Gist options
  • Save vuon9/d95c13e6095dbf0099fb3b76608e7b55 to your computer and use it in GitHub Desktop.
Save vuon9/d95c13e6095dbf0099fb3b76608e7b55 to your computer and use it in GitHub Desktop.
Grid Table with flexible columns
//
// GridTable.swift
//
// Created by Vuong Bui on 6/28/25.
//
import SwiftUI
// MARK: - Data Models
struct TableCell: Identifiable {
let id = UUID()
var content: String
var isHeader: Bool = false
}
struct TableRow: Identifiable {
let id = UUID()
var cells: [TableCell]
}
// MARK: - Main Table Component
struct FixedColumnTable: View {
let rows: [TableRow]
let rowHeight: CGFloat = 40
private var totalHeight: CGFloat {
CGFloat(rows.count) * rowHeight
}
var body: some View {
// Table content
Grid(alignment: .leading, horizontalSpacing: 0, verticalSpacing: 0) {
ForEach(rows) { row in
GridRow {
ForEach(row.cells) { cell in
CellContent(cell: cell)
}
}
Divider()
.gridCellUnsizedAxes([.horizontal])
}
}
}
}
// MARK: - Cell Component
struct CellContent: View {
let cell: TableCell
let maxWidth: CGFloat = 120
private var font: Font {
return cell.isHeader
? Font.system(size: 12, weight: .semibold)
: Font.system(size: 12)
}
var body: some View {
Group {
Text(cell.content)
.font(font)
.padding(3)
.frame(maxWidth: maxWidth, maxHeight: 18, alignment: .leading)
.truncationMode(.tail)
}
.background(cell.isHeader ? .gray.opacity(0.3): .secondary)
.foregroundColor(.primary)
}
}
struct FixedSizeTableView: View {
var data: (headers: [String], rows: [[String]])
var tableRows: [TableRow] {
// Create the header row
let headerRow = TableRow(
cells: data.headers.map {
TableCell(content: $0, isHeader: true)
}
)
// Create the data rows
let dataRows = data.rows.map { rowData in
TableRow(cells: rowData.map { TableCell(content: $0) })
}
return [headerRow] + dataRows
}
var body: some View {
FixedColumnTable(
rows: tableRows,
)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
FixedSizeTableView(data: (
headers: ["Header 1", "Header 2", "Header 3", "Header 4", "Header 5"],
rows: [
["Row 1, Cell 1", "Row 1, Cell 2 it's too long bro", "Row 1, Cell 3", "Row 1, Cell 4", "Row 1, Cell 5"],
["Row 2, Cell 1", "Row 2, Cell 2", "Row 2, Cell 3", "Row 2, Cell 4", "Row 2, Cell 5"],
["Row 3, Cell 1", "Row 3, Cell 2", "Row 3, Cell 3", "Row 3, Cell 4", "Row 3, Cell 5"],
["Row 1, Cell 1", "Row 1, Cell 2", "Row 1, Cell 3", "Row 1, Cell 4", "Row 1, Cell 5"],
["Row 2, Cell 1", "Row 2, Cell 2", "Row 2, Cell 3", "Row 2, Cell 4", "Row 2, Cell 5"],
["Row 1, Cell 1", "Row 1, Cell 2", "Row 1, Cell 3", "Row 1, Cell 4", "Row 1, Cell 5"],
["Row 2, Cell 1", "Row 2, Cell 2", "Row 2, Cell 3", "Row 2, Cell 4", "Row 2, Cell 5"],
["Row 3, Cell 1", "Row 3, Cell 2", "Row 3, Cell 3", "Row 3, Cell 4", "Row 3, Cell 5"],
["Row 1, Cell 1", "Row 1, Cell 2", "Row 1, Cell 3", "Row 1, Cell 4", "Row 1, Cell 5"],
["Row 2, Cell 1", "Row 2, Cell 2", "Row 2, Cell 3", "Row 2, Cell 4", "Row 2, Cell 5"],
]
)).previewLayout(.sizeThatFits)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment