Blog

How to Size SF Symbols Correctly in SwiftUI (Avoid resizable())

If you've ever tried to resize an SF Symbol the same way you'd resize a regular image, you've probably noticed things break in unexpected ways. The icon loses its proper alignment, or the symbol stops behaving like a symbol at all. There's a specific reason for that—and a straightforward fix that works every time.

Why resizable() Breaks SF Symbols

The standard approach for resizing images in SwiftUI is to call resizable() and then apply frame() or similar modifiers. That works perfectly for regular image assets, but it doesn't play well with SF Symbols.

Once you apply resizable() to an SF Symbol, the system stops treating it as a symbol. It becomes a plain image instead. That means you lose the built-in alignment behavior, and the icon no longer responds to text scaling the way symbols normally do. If you've ever seen an icon that looks slightly off next to your text, this is likely why.

The solution is counterintuitive if you're coming from regular SwiftUI image handling: don't use resizable(). Use font modifiers instead.

The Right Approach: font() and imageScale()

To size an SF Symbol correctly, you apply font() directly to the Image view. This preserves the symbol's identity while setting its display size.

You have two main options. The first is to use a semantic font, which ties the symbol's size to a standard text size:

“`

Image(systemName: "paperplane")

.font(.title)

“`

This makes the icon match the title text size automatically. When text scales system-wide, the symbol scales too.

The second option gives you explicit control with a point value:

“`

Image(systemName: "tray")

.font(.system(size: 20))

“`

Use this when you need a specific dimension that doesn't correspond to a named text style.

For fine-tuning how the symbol relates to its surrounding text, SwiftUI provides imageScale(). This modifier scales the symbol relative to its current font size:

“`

Image(systemName: "doc")

.font(.body)

.imageScale(.small)

“`

Available scale values are .small, .medium, and .large. Without an explicit font set, the symbol inherits sizing from the current environment—useful when you want symbols to adapt automatically to context.

Say you're building a settings screen with a list of toggles. Each row has a label and an icon. Using font modifiers keeps everything aligned properly, even when the user changes their system text size. The icons grow and shrink with the text, maintaining visual harmony across different accessibility settings.

Our Analysis

This is one of those details that trips up developers new to SwiftUI, especially those moving from UIKit where symbol handling worked differently. The instinct to reach for resizable() comes from years of working with standard images, but symbols require a different mental model.

Apple designed SF Symbols to be text-friendly. They align with baseline grids, respond to Dynamic Type, and adjust to bold or italic text contexts automatically. Forcing them into a resizable image pipeline strips away all of that. You're essentially taking a smart component and reducing it to dumb pixels.

The font() approach keeps symbols integrated with the text system. That's the win here: symbols that look right at every size and accessibility setting without extra work.

What to Watch Next

If you're maintaining older UIKit apps and want to gradually adopt SwiftUI, the techniques here transfer well. You can embed SwiftUI views with properly sized symbols inside UIKit view controllers using UIHostingController. That's worth exploring if you're not ready for a full migration but want modern icon rendering.

Key takeaways

  • Never apply resizable() to an SF Symbol in SwiftUI—it converts the symbol into a plain image and breaks alignment
  • Use font() with semantic styles (.title, .body) or explicit point sizes to control symbol dimensions
  • Apply imageScale() for fine-tuned scaling relative to the symbol's current font size
  • Proper sizing keeps symbols aligned with text and responsive to accessibility settings
  • These same principles apply when embedding SwiftUI symbol views inside UIKit projects

Frequently asked questions

Why does resizable() break SF Symbols in SwiftUI?

When you apply resizable() to an SF Symbol, the system stops treating it as a symbol and treats it as a regular image instead. This strips away symbol-specific behavior like automatic alignment with text and Dynamic Type support.

How do I set a specific size for an SF Symbol?

Use the font() modifier with an explicit point value, like .font(.system(size: 20)), or use a semantic font style like .font(.title) to match standard text sizes.

What does imageScale() do in SwiftUI?

The imageScale() modifier scales the symbol relative to its font size. It accepts .small, .medium, or .large, allowing you to fine-tune how the icon appears next to text without changing the underlying font size.

How do symbols behave when users change their system text size?

Symbols sized with font modifiers automatically respect the user's accessibility settings. If you use semantic fonts, symbols scale proportionally with text. With explicit point sizes, symbols stay fixed regardless of text size preferences.

Can these techniques help if I'm mixing SwiftUI into an existing UIKit app?

Yes. You can wrap SwiftUI views containing properly sized SF Symbols inside UIHostingController and embed them in UIKit view controllers. This lets you adopt modern symbol rendering incrementally without rewriting your entire interface.

Related