Adapting SwiftUI layouts for large Dynamic Type sizes
Learn how to restructure SwiftUI layouts when larger accessibility text sizes leave too little room for content.
05 Oct 2026 · 5 min read
A layout that works well at the default text size can become cramped when Dynamic Type increases the content size. Supporting Dynamic Type therefore also means allowing the layout to grow or change its structure.
In this article, we'll adapt a task row that works horizontally at standard text sizes but becomes difficult to read at larger accessibility sizes. For an introduction to scaling system and custom fonts, check out the article on supporting Dynamic Type in SwiftUI.

Starting with a horizontal layout
Suppose a task row displays its title next to a due date:
HStack(alignment: .firstTextBaseline, spacing: 12) {Text(title).font(.headline).frame(maxWidth: .infinity, alignment: .leading)Label(dueDate, systemImage: "calendar").font(.subheadline).foregroundStyle(.secondary)}.padding().background(.quaternary, in: .rect(cornerRadius: 12))
At a standard text size, both pieces of information have enough horizontal space. At an accessibility size, the title and due date grow while still sharing the same row. The title may wrap into a narrow column even though it would remain easy to read if it could use the full width.
To solve this, we can place the due date below the title when an accessibility size is active.
Switching layout for accessibility sizes
SwiftUI exposes the current text size through the dynamicTypeSize environment value. Its isAccessibilitySize property tells us whether the user selected one of the larger accessibility categories.
We can use that value to choose between HStackLayout and VStackLayout:
private struct TaskRow: View {@Environment(\.dynamicTypeSize) private var dynamicTypeSizeprivate var taskLayout: AnyLayout {if dynamicTypeSize.isAccessibilitySize {AnyLayout(VStackLayout(alignment: .leading, spacing: 8))} else {AnyLayout(HStackLayout(alignment: .firstTextBaseline, spacing: 12))}}var body: some View {taskLayout {Text(title).font(.headline).frame(maxWidth: .infinity, alignment: .leading)Label(dueDate, systemImage: "calendar").font(.subheadline).foregroundStyle(.secondary)}.padding()}}
AnyLayout lets both layouts contain the same views. At standard sizes, the title and date remain next to each other. At accessibility sizes, the vertical layout gives each of them the full row width.
How AnyLayout works
Although AnyLayout is a type-erased layout, it doesn't erase the child views in the way that AnyView erases a view. It only hides whether the active layout algorithm comes from HStackLayout, VStackLayout or another type conforming to the Layout protocol.
The trailing closure still builds the Text and Label as the same child views. A type conforming to Layout receives a LayoutSubviews collection containing a proxy for each child, which it can measure and position. AnyLayout forwards those proxies to the wrapped layout algorithm. When the selected layout changes, SwiftUI therefore rearranges the existing children instead of replacing their view hierarchy, preserving their identity and state.
Choosing when to change the layout
A narrow device, a long localized title or additional row content might require the vertical layout at an earlier text size.
If the decision is primarily about the available width rather than the selected text size, ViewThatFits may be a better choice because it selects a layout based on which version fits. The article on building adaptive layouts with ViewThatFits covers that approach in more detail.
Testing at larger text sizes
We can preview the same row at different Dynamic Type sizes by changing the environment:
#Preview("Default text size") {TaskRow(title: "Prepare the accessibility audit article",dueDate: "Today").padding()}#Preview("Accessibility 3") {TaskRow(title: "Prepare the accessibility audit article",dueDate: "Today").padding().environment(\.dynamicTypeSize, .accessibility3)}
Conclusion
Supporting Dynamic Type doesn't end when text grows with the user's preferred size. The surrounding layout also needs to give that text enough room.
isAccessibilitySize and AnyLayout keep that solution compact: one environment value drives the decision, and the same child views are reused in both layouts without losing their identity or state.



Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips



