Exploring SF Symbols: Where to Start and How to Use Them (Part 1)

1645 words • 7 minute read.

I recently took a deep dive into the current state of SF Symbols, and I thought I’d share my findings with you, especially since Apple introduced new SF Symbol animations at this year’s WWDC.

So what are SF symbols for anyone who doesn’t know?

SF Symbols is an iconography library created by Apple, first released in 2019 alongside iOS 13 at WWDC. The library, courtesy of Apple, is free to download here.

Screenshot of SF Symbols 6 running on MacOS.

Screenshot of SF Symbols 6 running on MacOS.

Every year since they have added further symbols and categories to the library, which at the time of writing has over 6000 symbols. The symbols are licenced by Apple but are free to use within your iOS apps. They give consistency and a professional feel so there is really no excuse not to be using them, unless you have your own designer to hand!

So with all of that in mind…

How can we show SF Symbols?

Take this sun icon, for example. This is called sun.max.fill .

An Image of sun.max.fill SF symbol.

An Image of sun.max.fill SF symbol.

In SwiftUI we can show this icon in a SwiftUI View , by using the Image initializer with the systemName property.

Image(systemName: "sun.max.fill")

How can we change the size of an SF Symbols?

There are three ways to adjust the size of an SF Symbol. The simplest is to use the .imageScale() modifier, which allows you to add a scale of .small, .medium and .large .

Image(systemName: "sun.max.fill")
    .imageScale(.small)

Image(systemName: "sun.max.fill")
    .imageScale(.medium)

Image(systemName: "sun.max.fill")
    .imageScale(.large)

This will give the below outcome:

SF Symbols with different image scales applied.

SF Symbols with different image scales applied.

Now because SF Symbols are integrated into the SF font family (although they are technically not a font, they are a vector based graphic), we can modify the size using the .font(.system(size:)) modifier:

Image(systemName: "sun.max.fill")
    .font(.system(size: 20))

Image(systemName: "sun.max.fill")
    .font(.system(size: 30))

Image(systemName: "sun.max.fill")
    .font(.system(size: 40))

Giving the below outcome:

SF Symbols with different system sizes applied.

SF Symbols with different system sizes applied.

I personally prefer modifying SF Symbol sizes with .font(.system(size:)) , because it provides finer control over the dimensions of the symbol.

Finally, you can also use .font(.system()) and pass in a Font.TextStyle .

Image(systemName: "sun.max.fill")
		.font(.system(.callout))
                    
Image(systemName: "sun.max.fill")
		.font(.system(.title))
                    
Image(systemName: "sun.max.fill")
		.font(.system(.largeTitle))

The above is not an exhaustive list, but an example of the different text styles you can apply. The above code will give you a very similar output to the .system(size:) example above. That doesn’t mean to say you should use one over the other, but which ever one suites your present application.

How can we change the weight of an SF Symbols?

Changing the weight of an SF Symbol can be done using the weight parameter on the .system() API.

Here’s a comprehensive list of the available weights for SF Symbols.

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .ultraLight))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .thin))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .light))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .regular))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .medium))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .semibold))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .bold))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .heavy))

Image(systemName: "sun.max.fill")
    .font(.system(.title, weight: .black))

And here is how they look when applied.

SF Symbols with different weights applied.

SF Symbols with different weights applied.

How can we change the colour of an SF Symbols?

We can change the colour of an SF Symbol using the .foregroundStyle() modifier.

Image(systemName: "sun.min.fill")
    .foregroundStyle(.red)

Image(systemName: "sun.min.fill")
    .foregroundStyle(.green)

Image(systemName: "sun.min.fill")
    .foregroundStyle(.blue)

You can use any colour available in the SwiftUI library.

SF Symbols with different foreground colours applied.

SF Symbols with different foreground colours applied.

Or you can use any custom colours that you may have added to the asset catalogue.

Image(systemName: "sun.min.fill")
		.foregroundStyle(Color("AccentColor", bundle: .main))

You can also add a gradient to an SF Symbols in the same way that we do to text, using the .foregroundStyle(Gradient(colors:)) modifier.

Image(systemName: "sun.min.fill")
    .foregroundStyle(Gradient(colors: [.red, .yellow]))

Image(systemName: "sun.min.fill")
    .foregroundStyle(Gradient(colors: [.blue, .green]))

Image(systemName: "sun.min.fill")
    .foregroundStyle(Gradient(colors: [.blue, .pink]))

SF Symbols with different gradients applied.

SF Symbols with different gradients applied.

How can we change the rendering mode of an SF Symbol?

We can change the rending mode of the symbol using the .rendingMode() modifer. The rending mode modifer effects how the image is rendered on screen. There are 2 rendering modes. .template and .original .

.template renders the image in a template of the original, meaning that a foregroundStyle can be applied over the top of it, changing the colour if required.

.original renders the image in its original form, meaning any foregroundStyle applied over the top of it may not have no effect. Some SF Symbols as you can see below, do have different colour elements set by default.

You will notice how the first two symbols are the same. The is because by default SF Symbols are rendered in either white or black, depending on the color scheme. .renderingMode(.template) is normally used when you wish to change the colour of a custom icon that has been added to the asset catalogue.

Image(systemName: "sun.rain.fill")
                    
Image(systemName: "sun.rain.fill")
		.renderingMode(.template)
                    
Image(systemName: "sun.rain.fill")
		.renderingMode(.original)

SF Symbols with different rendering modes applied.

SF Symbols with different rendering modes applied.

If we look at a symbol that only has the badge coloured in its original mode, we can still apply a foregroundStyle to the rest of the symbol.

Image(systemName: "person.crop.circle.badge.plus")

Image(systemName: "person.crop.circle.badge.plus")
    .renderingMode(.original)
    .foregroundColor(.blue)

SF Symbols with a rendering mode layered with a foreground style applied.

SF Symbols with a rendering mode layered with a foreground style applied.

How can we add a symbol variant to an SF Symbols?

Some SF Symbols come with different variants. They may have a .badge, .fill or .slash variant, although this is not an exhaustive list.

A variant can either be added directly in the name when we are initializing the symbol, or using the .symbolVariant() modifier. The modifiers can also be layer on top of one another. There is no right or wrong way to either method, I would use which ever method best suites the application.

Image(systemName: "speaker")

Image(systemName: "speaker.fill")

Image(systemName: "speaker")
    .symbolVariant(.fill)

Image(systemName: "speaker")
    .symbolVariant(.fill)
    .symbolVariant(.slash)

SF Symbols with different symbol variants applied.

SF Symbols with different symbol variants applied.

Conclusion

There’s a lot to unpack here! We’ve explored initializing SF Symbols, adjusting their size, changing their colour, applying gradients, experimenting with rendering modes, and using symbol variants.

I may have missed something, if I have please let me know 😉.

Next week I want to dive into animated SF Symbols, so make sure to check it out.

Thanks for stopping by 🙂.