Default Cell Configuration
Style fonts, colours, alignment, and backgrounds for every cell with a simple callback.
The defaultCellConfiguration callback gives you access to every cell as it's configured, letting you change fonts, colours, alignment, backgrounds — anything a UILabel and UICollectionViewCell can do.
For most styling needs, this handles everything with zero boilerplate. Need images, buttons, or entirely custom layouts? See the Custom Cells article.
What You Can Customise
The default DataCell contains a single UILabel called dataLabel. Through defaultCellConfiguration, you have full access to:
Label Properties (cell.dataLabel)
| Property | What It Controls |
|---|---|
font | Font family, size, weight |
textColor | Text colour |
textAlignment | Left, center, right, natural, justified |
numberOfLines | Line limit (0 for unlimited with wrapping) |
lineBreakMode | How text truncates or wraps |
attributedText | Rich text with multiple styles |
adjustsFontSizeToFitWidth | Shrink text to fit |
minimumScaleFactor | How much to shrink (0.5 = 50% minimum) |
Cell Properties
| Property | What It Controls |
|---|---|
cell.backgroundColor | Cell background colour |
cell.contentView.backgroundColor | Content area background |
cell.layer.borderWidth | Border thickness |
cell.layer.borderColor | Border colour (use .cgColor) |
cell.layer.cornerRadius | Rounded corners |
cell.alpha | Transparency |
The Four Parameters
Your callback receives everything you need to make styling decisions:
config.defaultCellConfiguration = { cell, value, indexPath, isHighlighted in
// cell: The DataCell instance to configure
// value: The DataTableValueType being displayed
// indexPath: .section = column index, .item = row index
// isHighlighted: true if this cell is in the currently sorted column
}| Parameter | Type | Use For |
|---|---|---|
cell | DataCell | Setting visual properties |
value | DataTableValueType | Conditional styling based on content |
indexPath | IndexPath | Per-column or per-row styling |
isHighlighted | Bool | Styling the sorted column differently |
Working with Values
DataTableValueType is an enum with cases for different data types. Extract the underlying value for conditional styling:
config.defaultCellConfiguration = { cell, value, _, _ in
// Get the string representation (works for all types)
let text = value.stringRepresentation
// Extract typed values (returns nil if type doesn't match)
if let number = value.doubleValue {
// Style based on numeric value
}
if let intValue = value.intValue {
// Style based on integer
}
}Styling Examples
Custom Font Throughout
config.defaultCellConfiguration = { cell, _, _, _ in
cell.dataLabel.font = UIFont(name: "Avenir-Medium", size: 14)
}Monospaced Numbers
Numbers in tables look better when digits align vertically:
config.defaultCellConfiguration = { cell, value, _, _ in
// Use monospaced digits for numeric columns
if value.doubleValue != nil {
cell.dataLabel.font = .monospacedDigitSystemFont(ofSize: 14, weight: .regular)
} else {
cell.dataLabel.font = .systemFont(ofSize: 14)
}
}Colour-Code Negative Numbers
config.defaultCellConfiguration = { cell, value, _, _ in
if let number = value.doubleValue {
if number < 0 {
cell.dataLabel.textColor = .systemRed
} else if number > 0 {
cell.dataLabel.textColor = .systemGreen
} else {
cell.dataLabel.textColor = .secondaryLabel
}
} else {
cell.dataLabel.textColor = .label
}
}Per-Column Styling
indexPath.section is the column index. Style specific columns differently:
config.defaultCellConfiguration = { cell, _, indexPath, _ in
switch indexPath.section {
case 0: // ID column - monospaced, muted
cell.dataLabel.font = .monospacedSystemFont(ofSize: 12, weight: .regular)
cell.dataLabel.textColor = .secondaryLabel
case 2: // Status column - centered, bold
cell.dataLabel.textAlignment = .center
cell.dataLabel.font = .systemFont(ofSize: 14, weight: .semibold)
case 5: // Price column - right-aligned
cell.dataLabel.textAlignment = .right
default:
cell.dataLabel.font = .systemFont(ofSize: 14)
cell.dataLabel.textColor = .label
cell.dataLabel.textAlignment = .natural
}
}Alternating Row Colours (Zebra Stripes)
indexPath.item is the row index. Use modulo for alternating patterns:
config.defaultCellConfiguration = { cell, _, indexPath, _ in
if indexPath.item % 2 == 0 {
cell.contentView.backgroundColor = .systemBackground
} else {
cell.contentView.backgroundColor = .secondarySystemBackground
}
}Highlight Sorted Column
isHighlighted is true for cells in the column currently being sorted:
config.defaultCellConfiguration = { cell, _, indexPath, isHighlighted in
if isHighlighted {
cell.contentView.backgroundColor = .systemYellow.withAlphaComponent(0.15)
cell.dataLabel.font = .systemFont(ofSize: 14, weight: .medium)
} else {
cell.contentView.backgroundColor = indexPath.item % 2 == 0
? .systemBackground
: .secondarySystemBackground
cell.dataLabel.font = .systemFont(ofSize: 14, weight: .regular)
}
}Status Badges with Background Colours
config.defaultCellConfiguration = { cell, value, indexPath, _ in
// Only apply badge styling to the Status column (index 3)
guard indexPath.section == 3 else { return }
let status = value.stringRepresentation.lowercased()
switch status {
case "active":
cell.dataLabel.textColor = .systemGreen
cell.contentView.backgroundColor = .systemGreen.withAlphaComponent(0.1)
case "pending":
cell.dataLabel.textColor = .systemOrange
cell.contentView.backgroundColor = .systemOrange.withAlphaComponent(0.1)
case "inactive":
cell.dataLabel.textColor = .systemGray
cell.contentView.backgroundColor = .systemGray.withAlphaComponent(0.1)
default:
break
}
cell.dataLabel.textAlignment = .center
cell.dataLabel.font = .systemFont(ofSize: 12, weight: .semibold)
}Shrink-to-Fit Long Text
config.defaultCellConfiguration = { cell, _, _, _ in
cell.dataLabel.adjustsFontSizeToFitWidth = true
cell.dataLabel.minimumScaleFactor = 0.7 // Shrink to 70% minimum
}Rich Text with AttributedString
config.defaultCellConfiguration = { cell, value, indexPath, _ in
// Only for the description column
guard indexPath.section == 2 else { return }
let text = value.stringRepresentation
let attributed = NSMutableAttributedString(string: text)
// Highlight "URGENT" in red
if let range = text.range(of: "URGENT") {
let nsRange = NSRange(range, in: text)
attributed.addAttributes([
.foregroundColor: UIColor.systemRed,
.font: UIFont.boldSystemFont(ofSize: 14)
], range: nsRange)
}
cell.dataLabel.attributedText = attributed
}Combining with Colour Arrays
SwiftDataTables has built-in support for alternating row colours via highlightedAlternatingRowColors and unhighlightedAlternatingRowColors. These work in layers:
- Layer 1: Colour arrays — Applied first as baseline row colours
- Layer 2: defaultCellConfiguration — Applied second, can override backgrounds
This means you can use colour arrays for basic zebra striping while using defaultCellConfiguration for text styling:
var config = DataTableConfiguration()
// Layer 1: Alternating backgrounds (applied automatically)
config.highlightedAlternatingRowColors = [
.systemYellow.withAlphaComponent(0.1),
.systemYellow.withAlphaComponent(0.2)
]
config.unhighlightedAlternatingRowColors = [
.systemBackground,
.secondarySystemBackground
]
// Layer 2: Text styling only (doesn't touch backgrounds)
config.defaultCellConfiguration = { cell, value, indexPath, _ in
// Monospaced for numbers
if value.doubleValue != nil {
cell.dataLabel.font = .monospacedDigitSystemFont(ofSize: 14, weight: .regular)
cell.dataLabel.textAlignment = .right
}
// Red for negative
if let num = value.doubleValue, num < 0 {
cell.dataLabel.textColor = .systemRed
}
}Override order If you set cell.contentView.backgroundColor in defaultCellConfiguration, it overrides the colour arrays. Leave backgrounds alone if you want the arrays to work.
Resetting to Defaults
Cells are reused. If you conditionally set properties (e.g., red text for negative numbers), you must also set the default case, or stale styles persist:
// BAD: Stale red color persists when cell is reused
config.defaultCellConfiguration = { cell, value, _, _ in
if let num = value.doubleValue, num < 0 {
cell.dataLabel.textColor = .systemRed // Set for negatives...
}
// ...but never reset for positives!
}
// GOOD: Always set both cases
config.defaultCellConfiguration = { cell, value, _, _ in
if let num = value.doubleValue, num < 0 {
cell.dataLabel.textColor = .systemRed
} else {
cell.dataLabel.textColor = .label // Explicit reset
}
}The cell's prepareForReuse() resets font, textColor, textAlignment, and backgroundColor to defaults. But if you set properties conditionally, always handle both the "on" and "off" cases.
Performance Considerations
defaultCellConfiguration is called for every visible cell during scrolling. Keep it fast:
- Avoid heavy computation — Don't do network calls, complex calculations, or string parsing
- Cache fonts — Create
UIFontinstances once, store in a property - Simple conditionals — Switch statements and if/else are fine
- No layout changes — Don't modify constraints; that's what custom cells are for
class MyViewController: UIViewController {
// Cache fonts instead of creating them in the callback
private let monoFont = UIFont.monospacedDigitSystemFont(ofSize: 14, weight: .regular)
private let bodyFont = UIFont.systemFont(ofSize: 14)
private lazy var config: DataTableConfiguration = {
var config = DataTableConfiguration()
config.defaultCellConfiguration = { [weak self] cell, value, _, _ in
guard let self = self else { return }
cell.dataLabel.font = value.doubleValue != nil ? self.monoFont : self.bodyFont
}
return config
}()
}When You Need Custom Cells Instead
defaultCellConfiguration can handle most styling needs, but switch to custom cells when you need:
- Multiple UI elements — Images + text, icon + label, stacked views
- Interactive controls — Buttons, switches, sliders
- Different layouts per column — Avatar in column 1, text in column 2, button in column 3
- Complex Auto Layout — Anything beyond a single label
If all you need is styling (fonts, colours, alignment, backgrounds), stick with defaultCellConfiguration. It's simpler and performs better.