How to Build Adaptive iOS Table Views with Self-Sizing Cells and Dynamic Type

Implement self-sizing cells and Dynamic Type in iOS by configuring UITableView with automaticDimension and using UIFont text styles for labels.

How Self-Sizing Cells Work in UITableView

Self-sizing cells use Auto Layout constraints to determine row heights automatically. The UITableView calls systemLayoutSizeFittingSize on each cell, which computes the optimal height based on the cell's content and layout constraints. Three conditions enable this behavior: complete Auto Layout constraints, an estimated row height, and rowHeight set to UITableView.automaticDimension.

Configure the table view in viewDidLoad:

tableView.estimatedRowHeight = 95.0
tableView.rowHeight = UITableView.automaticDimension

Set each label's numberOfLines to 0 in Interface Builder or in code. This allows labels to expand based on content length. Without zero-line labels, self-sizing cells truncate content at the default single line.

Setting Up Complete Auto Layout Constraints

Self-sizing requires unambiguous constraints pinning content to all four edges of the cell's content view. Use stack views for simpler constraint management. Embed labels in a UIStackView with .vertical axis, then pin the stack view to all four edges with consistent margins.

Interface Builder may show layout ambiguity warnings. Click the warning icon in the Document Outline and select Content Priority Ambiguity to resolve compression resistance and hugging priority conflicts.

Configuring Dynamic Type with Text Styles

Dynamic Type, introduced in iOS 7, lets your app respond to the user's preferred text size. Users adjust text size in Settings > Display & Brightness > Text Size or Settings > General > Accessibility > Larger Text for accessibility sizes.

Adopt Dynamic Type by using UIFont text styles instead of fixed font sizes. In Interface Builder, change each label's Font from a custom value to a Text Style. For a list-based app:

  • Title labels use the Headline style
  • Subtitle labels use the Subhead style
  • Body labels use the Body style

In code, use UIFont.preferredFont(forTextStyle:):

nameLabel.font = UIFont.preferredFont(forTextStyle: .headline)
addressLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
descriptionLabel.font = UIFont.preferredFont(forTextStyle: .body)

Enabling Automatic Font Size Adjustment

Set adjustsFontForContentSizeCategory to true so labels update when the user changes text size. In Interface Builder, check Automatically Adjusts Font in the Attributes inspector. In code:

nameLabel.adjustsFontForContentSizeCategory = true
addressLabel.adjustsFontForContentSizeCategory = true
descriptionLabel.adjustsFontForContentSizeCategory = true

Before iOS 11, you manually observed UIContentSizeCategoryDidChangeNotification and reloaded the table view. Modern iOS handles this automatically when adjustsFontForContentSizeCategory is enabled.

Combining Self-Sizing Cells with Dynamic Type

Self-sizing cells and Dynamic Type work together. When a user increases text size, labels grow and the cell expands to accommodate the larger text. The table view recalculates heights automatically. Without self-sizing, larger text overflows or gets clipped.

This combination requires no additional code beyond the configurations above. The table view adapts to both content length and accessibility preferences simultaneously.

Testing Accessibility and Dynamic Type

Test in the iOS Simulator by navigating to Settings > Accessibility > Larger Text. Enable Larger Accessibility Sizes and drag the slider. Switch back to your app. Text sizes update immediately without restarting.

Test with both the smallest and largest text sizes. Verify that all labels remain fully visible and that cells expand correctly. Pay attention to overlapping elements or truncated content at extreme sizes.

Best Practices for Self-Sizing Cells

Provide accurate estimated row heights. The estimatedRowHeight value affects scroll bar sizing and scrolling performance. An estimate close to the actual average height produces smooth scrolling. An inaccurate estimate causes the scroll bar to jump.

Define constraints to all four edges of the content view. Missing horizontal or vertical constraints cause ambiguous layouts and incorrect cell sizing. Stack views simplify this by managing internal constraints automatically.

Set numberOfLines to 0 on every label that contains variable-length text. Labels default to single-line display. Text beyond the available width truncates with an ellipsis.

Test with different content lengths. Verify cells display correctly with very short content and very long content. Both extremes expose layout issues.

Best Practices for Dynamic Type

Use UIFontMetrics for custom fonts. If your app uses custom fonts, scale them with Dynamic Type using UIFontMetrics.default.scaledFont(for:). This ensures custom fonts respond to accessibility size changes.

let customFont = UIFont(name: "CustomFont", size: 16)!
nameLabel.font = UIFontMetrics.default.scaledFont(for: customFont)
nameLabel.adjustsFontForContentSizeCategory = true

Support accessibility sizes. Users with visual impairments enable Larger Accessibility Sizes. Ensure your layout accommodates these extreme sizes without breaking. Labels should not overlap or extend beyond the cell bounds.

Use traitCollectionDidChange to respond to content size category changes. Override this method and update constraints or spacing programmatically if you need custom layout adjustments when text size changes.

Common Pitfalls

Cells not resizing. Verify rowHeight is set to UITableView.automaticDimension and estimatedRowHeight has a value. Check that labels have numberOfLines set to 0 and constraints pin all four edges.

Dynamic Type not updating. Ensure adjustsFontForContentSizeCategory is true on every label. In iOS versions before 11, add an observer for UIContentSizeCategoryDidChangeNotification and call tableView.reloadData().

Scrolling performance issues. Inaccurate estimated row heights cause the table view to recalculate continuously. Measure your average content height and set estimatedRowHeight accordingly.

Layout ambiguity warnings. Use Interface Builder's Resolve Auto Layout Issues tool. Stack views reduce ambiguity by managing internal constraints automatically.

Summary

This tutorial covered the complete implementation process from setup to working code. You now have a functional implementation that you can extend and customize for your own projects. The same patterns apply to related features in the iOS ecosystem.

Frequently Asked Questions

Can I test this on the iOS Simulator? Some features require a physical device with a compatible processor. The article notes specific sections where the Simulator is sufficient and where a real device is required.

What is UITableView? UITableView is the UIKit class that displays scrolling, single-column lists of rows. It is part of iOS, so there is nothing to install.

What iOS version is required? Self-sizing cells work from iOS 8, and scaling fonts with UIFontMetrics needs iOS 11 or later.

What are the prerequisites for this tutorial? You need Xcode installed on a Mac and basic familiarity with Swift programming. No prior experience with the specific framework is required.

All articles