Highlight
tvOS 27 introduces system-level Larger Text support for the first time. By replacing hard-coded fonts with semantic text styles and fixed constraints with flexible layouts, developers can let apps automatically adapt across seven text sizes from Large to Accessibility XXXL. Apps can also declare Larger Text support in App Store Accessibility Nutrition Labels to reach users who need it.
Key Takeaways
Imagine browsing movies on Apple TV from the couch. Your parents walk over, squint, and say, “The text is too small. I can’t read it.” You open Settings and increase the text size to the maximum, but half the title is clipped, button labels crowd together, and the whole app becomes unusable.
That was the normal state for every app before tvOS 27. iOS has supported Dynamic Type for years, but Apple TV was missing it. tvOS 27 finally fills that gap and brings system-level font scaling to the living room screen.
Users can go to Settings > Accessibility > Display & Text Size > Text Size and choose anything from Large through Accessibility XXXL. Standard UIKit and SwiftUI components such as Label, Button, and Navigation TabBar respond to that setting automatically. Your remaining job is to find custom, hard-coded text and layouts, then make them flexible.
Apple also provides an extra discovery surface: you can mark Larger Text support for tvOS in App Store Accessibility Nutrition Labels. That makes your app easier to find for users who actively look for accessible apps.
Three Issues You Must Audit
When you run your app in Larger Text mode, focus on three categories of problems.
The first is hard-coded font sizes. For example, a title using .system(size: 28) will not change no matter what the user selects in Settings. In Larger Text mode it will look out of place, and it may even be smaller than the surrounding text.
The second is fixed width and height constraints. A Text view locked to frame(width: 300) can only truncate when the text gets bigger. Or an HStack with spacing hard-coded to 40 pt may not have enough room once the text grows, causing content to crowd together.
The third is layout that does not adapt to text size. A poster wall may show six items per row at the standard size. Once text grows, the title under each poster gets longer and six columns no longer fit. If you do not reduce the column count, titles get clipped and the whole interface feels cramped.
Details
From Hard-Coded Values to Semantic Styles (04:58)
The session demonstrates the idea with a media app’s “Signup information” screen. The original code looks like this:
VStack(spacing: 20) {
Text("Signup information")
.font(.caption.bold())
.lineLimit(1)
.foregroundStyle(.secondary)
.frame(width: 300, alignment: .leading)
HStack(alignment: .top, spacing: 40) {
// ...
}
}
There are three problems here: .font(.caption.bold()) uses a semantic style, but other parts of the view hierarchy still contain hard-coded font sizes; .frame(width: 300) locks the width; and spacing: 40 may not be enough once the text grows.
The first fix is to replace the fixed width with a flexible constraint.
VStack(spacing: 20) {
Text("Signup information")
.font(.caption.bold())
.lineLimit(1)
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
HStack(alignment: .top, spacing: 40) {
// ...
}
}
Key points:
maxWidth: .infinitytells SwiftUI to give this Text as much width as it needs instead of truncating.- The semantic
.captionstyle automatically scales with Dynamic Type. You do not need to calculate a scale factor yourself.
Dynamic Type in UIKit (05:55)
If you use UIKit, the approach is similar, but there is one extra switch:
// Hard-coded font size. Do not do this.
titleLabel.font = UIFont.boldSystemFont(ofSize: 28)
// The right approach
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
titleLabel.adjustsFontForContentSizeCategory = true
Key points:
UIFont.preferredFont(forTextStyle:)returns a font size that matches the user’s setting.adjustsFontForContentSizeCategory = truelets the label update automatically when the user changes text size in Settings, with no app restart required.
Use Container-Relative Frames to Adjust Grid Columns (07:09)
Poster walls are one of the most common tvOS app surfaces. Six columns may feel spacious at the standard text size, but Larger Text often needs four columns so each poster title has enough room.
struct MovieShelf: View {
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
var body: some View {
ScrollView(.horizontal) {
LazyHStack(spacing: 40) {
ForEach(Asset.allCases) { asset in
Button {
// ...
} label: {
asset.portraitImage
Text(asset.title)
}
.containerRelativeFrame(
.horizontal,
count: dynamicTypeSize.isAccessibilitySize ? 4 : 6,
spacing: 40)
}
}
}
}
}
Key points:
@Environment(\.dynamicTypeSize)reads the current text size.dynamicTypeSize.isAccessibilitySizechecks whether the user has selected an Accessibility text size from AX1 through AX5.containerRelativeFrame(.horizontal, count:spacing:)divides the ScrollView’s available width across the requested number of child views and handles spacing automatically.- When
isAccessibilitySizeis true, the shelf shows four columns; otherwise it shows six.
If titles are especially long, you can also consider a custom marquee effect that scrolls text horizontally while the item has focus.
Switch Conditional Layouts with AnyLayout (08:07)
Content cards often place an image and text side by side. At Larger Text sizes, that horizontal layout can squeeze the text. The solution is to switch to a vertical stack for accessibility text sizes.
struct CardContentView: View {
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
var asset: Asset
var body: some View {
let layout = dynamicTypeSize.isAccessibilitySize ?
AnyLayout(VStackLayout(alignment: .leading, spacing: 10)) :
AnyLayout(HStackLayout(alignment: .top, spacing: 10))
layout {
asset.image
Text(asset.title)
Text(asset.subtitle)
.foregroundStyle(.secondary)
}
}
}
Key points:
AnyLayoutis SwiftUI’s type-erased layout container. It lets you switch layout strategies at runtime.- Using
AnyLayoutinstead of anif/elsethat swaps betweenVStackandHStackpreserves view identity, which prevents the tvOS focus system from losing the current focus during layout changes. VStackLayoutandHStackLayoutare concrete implementations of the layout protocol, and both can be wrapped by AnyLayout.
Respond to Content Size Changes in UIKit (08:31)
UIKit listens for font size changes through Trait Changes:
class AdaptiveLayoutViewController: UIViewController {
let stackView = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
updateLayout()
let sizeTraits: [UITrait] = [UITraitPreferredContentSizeCategory.self]
registerForTraitChanges(sizeTraits, action: #selector(updateLayout))
}
@objc private func updateLayout() {
if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
stackView.axis = .vertical
} else {
stackView.axis = .horizontal
}
}
}
Key points:
UITraitPreferredContentSizeCategory.selfis the trait type to observe.registerForTraitChanges(_:action:)replaces the olderNSNotificationapproach on iOS/tvOS 17 and later, and it is more type-safe.traitCollection.preferredContentSizeCategory.isAccessibilityCategorychecks whether the current size is an Accessibility text size.- Switching
UIStackView.axisbetween.horizontaland.verticalachieves the same effect as SwiftUI’sAnyLayout.
Ideas to Build
A TV shopping app that works for older adults
What to build: When Larger Text is enabled, change product cards from a horizontal arrangement to a vertical stack, let price information scale automatically with the .headline style, and expand product descriptions from a one-line summary to multiple lines.
Why it is worth doing: Older users often need Larger Text most, but fixed layouts tend to truncate or overlap at larger sizes. Flexible layouts keep the interface usable at every text size and expand the audience for the app.
How to start: Read the current text size with @Environment(\.dynamicTypeSize), then use AnyLayout to switch from HStackLayout at standard sizes to VStackLayout at Larger Text sizes.
An adaptive poster wall for a video-on-demand app
What to build: Use a six-column poster wall at standard text sizes, then reduce it to four columns with containerRelativeFrame for Larger Text so long titles have enough room. If titles are still too long, start a marquee scroll when the poster gets focus.
Why it is worth doing: Movie title length varies a lot, and a fixed column count causes truncation at Larger Text sizes. Adaptive column counts preserve readability across text sizes.
How to start: Detect text size with @Environment(\.dynamicTypeSize), then use containerRelativeFrame(.horizontal, count:spacing:) to show four columns when isAccessibilitySize is true and six otherwise.
Declare accessibility support on the settings page
What to build: In App Store Connect Accessibility Nutrition Labels, mark Larger Text support for the tvOS app.
Why it is worth doing: This is a zero-code change, but it can surface your app in accessibility filters. Users who specifically look for accessible apps can find it more easily.
How to start: Sign in to App Store Connect, open the app information page’s Accessibility section, and select Larger Text support for tvOS.
Clean up legacy code with regular expressions
What to build: Search an older project for UIFont.systemFont(ofSize: and .system(size:, then replace them with UIFont.preferredFont(forTextStyle:) and semantic text styles. Search for frame(width: and frame(height: and evaluate which constraints can become flexible.
Why it is worth doing: Hard-coded font sizes and fixed constraints are the biggest obstacles to Dynamic Type support in legacy code. Batch cleanup is the shortest path to giving an older app Larger Text support.
How to start: Use your IDE’s global search for those patterns, then replace each case with semantic styles and flexible constraints such as maxWidth: .infinity or minHeight:.
Test font adaptation with the tvOS focus system
What to build: On an Apple TV, set text size to the largest value, navigate through every screen, and verify that focus rings fully wrap content, text is not clipped, and layouts do not shift incorrectly.
Why it is worth doing: tvOS focus navigation is sensitive to layout changes. At Larger Text sizes, focus rings can be offset or incomplete. Real-device testing finds problems the simulator may miss.
How to start: In Apple TV Settings, set text size to Accessibility XXXL. Use the remote to traverse every screen, focusing on button labels, poster titles, and detail-page body text.
Related Sessions
- 219: What’s new in accessibility for reading - Learn about new reading accessibility features on iOS and macOS, and build a cross-platform accessibility strategy alongside Larger Text on tvOS
- 220: Create custom accessibility controls - Learn how to add accessibility support to custom tvOS controls and combine it with Dynamic Type for a complete accessibility experience
- 278: UIKit modernization - UIKit modernization improvements, including more ways to use new APIs such as
registerForTraitChanges - 269: What’s new in SwiftUI - A tour of new SwiftUI features, including the full capabilities of
AnyLayoutandcontainerRelativeFrame
Comments
GitHub Issues · utterances