GitHub

Column Widths

Control how column widths are calculated to fit your content perfectly.

Column widths directly affect readability. Too narrow and text gets truncated — users see "John Sm..." instead of "John Smith". Too wide and columns waste space, forcing unnecessary horizontal scrolling.

SwiftDataTables calculates column widths automatically based on your content. You control how it calculates them — from simple fixed widths to intelligent content-aware measurement.

The Three Modes

Every column uses one of three width modes:

ModeHow It WorksBest For
.fixed(width:)Every cell gets the exact width you specifyKnown content, ID columns, buttons
.fitContentText(strategy:)Estimates width from text character countsLarge datasets with simple text cells
.fitContentAutoLayout(sample:)Measures cells using Auto LayoutCustom cells, complex layouts, small datasets

Fixed Width

The simplest option. You specify the exact width in points, and every cell in that column gets that width.

var config = DataTableConfiguration()
config.columnWidthMode = .fixed(width: 120)  // All columns 120pt wide

When to use: ID columns, status badges, action buttons — anywhere the content width is predictable and consistent.

Auto Layout Measurement

The most accurate option. SwiftDataTables creates a sizing cell, configures it with your content, and asks Auto Layout how wide it needs to be (systemLayoutSizeFitting). This works with any cell complexity — multi-line text, images, nested stacks, Dynamic Type.

config.columnWidthMode = .fitContentAutoLayout(sample: .sampledMax(sampleSize: 50))

The sample parameter controls how many cells are measured:

SampleMeasuresUse Case
.allEvery cell in the columnSmall datasets (<100 rows) or exact sizing required
.sampledMax(sampleSize: 50)50 evenly-distributed cells, uses the maximumGood balance of accuracy and performance
.percentile(0.95, sampleSize: 50)50 cells, uses 95th percentile widthIgnores extreme outliers that would over-widen columns

For most apps, .sampledMax(sampleSize: 50) is the sweet spot — fast enough for large datasets, accurate enough to avoid truncation.

Text Estimation Strategies

For simple text cells (no images, no complex layouts), text estimation is faster than Auto Layout. It calculates width from character counts rather than measuring rendered cells.

config.columnWidthMode = .fitContentText(strategy: .hybrid(sampleSize: 100, averageCharWidth: 7))

Six strategies are available, trading off accuracy vs performance:

StrategyHow It WorksPerformanceAccuracy
.maxMeasuredMeasures every cell's text, uses maximumSlowestHighest
.sampledMax(sampleSize:)Measures a sample, uses maximumFastGood
.percentileMeasured(percentile:, sampleSize:)Measures a sample, uses Nth percentileFastGood (ignores outliers)
.estimatedAverage(averageCharWidth:)charCount × avgCharWidth, averagedFastestModerate
.hybrid(sampleSize:, averageCharWidth:)Estimation validated against sampleFastGood
.fixed(width:)Explicit width, ignores contentInstantN/A

Choosing a Strategy

  • Under 100 rows — Use .maxMeasured for perfect accuracy
  • 100-10,000 rows — Use .hybrid or .sampledMax for balanced performance
  • 10,000+ rows — Use .estimatedAverage for speed, accept some truncation on outliers
  • Data with outliers — Use .percentileMeasured(0.95, sampleSize: 100) to ignore the widest 5%

The averageCharWidth Parameter

Estimation strategies use averageCharWidth to convert character counts to points. The default is 7, which works for most system fonts at standard sizes.

  • Monospace fonts — Characters are uniform width. Measure one character and use that exact value.
  • Proportional fonts — Characters vary ("W" is wider than "i"). Use 7-8 for body text, adjust based on your font.
  • Large text — Scale proportionally. If you're using 24pt font instead of 14pt, increase averageCharWidth accordingly.

Per-Column Overrides

Different columns often need different width strategies. An ID column should be fixed-width, while a description column should auto-size. Use columnWidthModeProvider to customize per column:

config.columnWidthModeProvider = { columnIndex in
    switch columnIndex {
    case 0:  // ID column — always 60pt
        return .fixed(width: 60)
    case 1:  // Name column — measure with Auto Layout
        return .fitContentAutoLayout(sample: .sampledMax(sampleSize: 50))
    case 4:  // Actions column — fixed width for buttons
        return .fixed(width: 100)
    default:
        return nil  // Use the global columnWidthMode
    }
}

Returning nil tells the column to use the global columnWidthMode setting.

Minimum and Maximum Widths

Constrain calculated widths to a range:

config.minColumnWidth = 80   // Columns won't shrink below 80pt
config.maxColumnWidth = 300  // Columns won't grow beyond 300pt
PropertyDefaultEffect
minColumnWidth70Prevents columns from becoming too narrow to read
maxColumnWidthnil (no limit)Prevents columns from becoming excessively wide

Header width always wins The header text (plus sort indicator) sets a minimum width that can exceed minColumnWidth. A column titled "Department" won't shrink narrower than that word, even if all data values are shorter.

Scale to Fill

By default, if your calculated column widths don't fill the table's frame, columns scale up proportionally to fill the space. This prevents awkward empty space on the right side.

// Default: columns expand to fill available width
config.shouldContentWidthScaleToFillFrame = true

// Disable: columns use their calculated widths exactly
config.shouldContentWidthScaleToFillFrame = false

When disabled, columns keep their calculated widths. If the total is less than the table width, empty space appears on the right. If the total exceeds the table width, horizontal scrolling is enabled.

Real-World Example

A typical configuration for a mixed table — some columns fixed, some auto-sized, with reasonable constraints:

var config = DataTableConfiguration()

// Global default: text estimation for performance
config.columnWidthMode = .fitContentText(strategy: .hybrid(sampleSize: 100, averageCharWidth: 7))

// Constraints
config.minColumnWidth = 60
config.maxColumnWidth = 250

// Per-column overrides
config.columnWidthModeProvider = { columnIndex in
    switch columnIndex {
    case 0:  // Row number
        return .fixed(width: 50)
    case 5:  // Description — needs Auto Layout for wrapping
        return .fitContentAutoLayout(sample: .sampledMax(sampleSize: 30))
    default:
        return nil
    }
}

let columns: [DataTableColumn<Product>] = [
    .init("#") { String($0.rowNumber) },
    .init("SKU", \.sku),
    .init("Name", \.name),
    .init("Category", \.category),
    .init("Price", \.price) { String(format: "$%.2f", $0) },
    .init("Description", \.description)  // Uses Auto Layout override
]