Adapting images and symbols to Dynamic Type sizes in SwiftUI
Dynamic Type allows users to adjust text size across the system, improving readability and accessibility. When we use built-in fonts in SwiftUI, text scales automatically to match the user’s preferred size. However, text is only part of the interface. Other elements, such as images and symbols, also need to adapt to maintain a balanced layout.
In this post, we'll look at how to handle images in a Dynamic Type environment, covering both SF Symbols and custom images. We'll explore techniques to ensure meaningful icons remain clear at larger text sizes, scale custom images appropriately, and exclude decorative images when necessary to optimize space.
# Using SF Symbols with Dynamic Type
If we use icons to communicate important information, they need to stay clear and legible at larger font sizes. SF Symbols make this easy by scaling automatically with Dynamic Type, ensuring they adjust as text size changes.
These symbols integrate with the San Francisco system font, aligning naturally with different weights and sizes. They work well for conveying concepts or representing objects, especially alongside text.
Even though SF Symbols are placed inside an Image
view, they behave more like text. They inherit font attributes from the environment to match surrounding content.
VStack {
Image(systemName: "cloud.sun")
Text("Partly Cloudy")
}
.font(.title)


An important detail to keep in mind is that it's generally not recommended to use the resizable()
modifier with SF Symbols. Applying resizable()
to a symbol causes it to lose its symbol properties, meaning it will be treated more like a regular image. This prevents it from scaling automatically with Dynamic Type.
VStack {
Image(systemName: "cloud.sun")
.resizable()
.scaledToFit()
.frame(height: 28)
Text("Partly Cloudy")
}
.font(.title)


There may be cases where converting a symbol into an image and manually scaling it, as we would with custom images, is appropriate. However, in most situations, it's best to use SF Symbols as intended to ensure they remain consistent with surrounding text and provide the best user experience.
# Scaling custom images
Custom images from an asset catalog don't automatically resize with text. If an image is not purely decorative but conveys meaning, we should manually ensure it scales appropriately when the user adjusts their preferred font size.
SwiftUI provides the ScaledMetric API to make this easier to handle. Instead of defining multiple sizes for an image, we can specify a base value and store it in a property annotated with the @ScaledMetric
property wrapper. By default, ScaledMetric
adjusts relative to the body
text style, but we can customize it to match a different text style.
In the example below, the image height will scale relative to the title
font size.
struct ContentView: View {
@ScaledMetric(relativeTo: .title)
private var imageHeight = 28
var body: some View {
VStack {
Image(.partlyCloudy)
.resizable()
.scaledToFit()
.frame(height: imageHeight)
Text("Partly Cloudy")
}
.font(.title)
}
}


When designing with scaled metrics, it's important to start with a size that works well for the default Dynamic Type setting and ensure that images have a high enough resolution to scale up without losing quality.
# Handling decorative images
Decorative images that don't convey important meaning shouldn't be scaled, as they can take up valuable screen space that could be used for more important content. In some cases, it may even be best to remove decorative images altogether at larger text sizes.
We can determine the current Dynamic Type size by reading the dynamicTypeSize environment value. In the example below, the image remains visible for standard text sizes but is removed entirely when an accessibility text size is enabled, ensuring that text has as much space as possible.
struct ContentView: View {
@Environment(\.dynamicTypeSize)
private var typeSize
var body: some View {
VStack {
if !typeSize.isAccessibilitySize {
Image(decorative: "forecast")
}
Text("Weather Forecast")
}
.font(.title)
}
}


Adapting images and symbols to Dynamic Type ensures that interfaces remain clear, accessible, and visually balanced across different text sizes. By making thoughtful adjustments to how images scale, or whether they appear at all, we can create layouts that adapt seamlessly to user preferences while maintaining clarity and usability.
If you want to build a strong foundation in SwiftUI, my new book SwiftUI Fundamentals takes a deep dive into the framework’s core principles and APIs to help you understand how it works under the hood and how to use it effectively in your projects.
For more resources on Swift and SwiftUI, check out my other books and book bundles.