Created
June 29, 2025 07:03
-
-
Save vuon9/d95c13e6095dbf0099fb3b76608e7b55 to your computer and use it in GitHub Desktop.
Grid Table with flexible columns
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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