Streaming is available in most browsers,
and in the Developer app.
-
SF Symbols in SwiftUI
Discover how you can incorporate SF Symbols into your SwiftUI app. We'll explore basic techniques for presenting symbols, customizing their size, and showing different variants. We'll also take you through the latest updates to symbol colorization and help you pick the right tool for your app's needs.
Resources
Related Videos
WWDC21
-
Download
♪ ♪ Hey, I'm Jacob. Welcome to "SF Symbols in SwiftUI." We're going to look at a few topics during this session. First, some of the fundamentals of using SF Symbols from SwiftUI. Then, how to use variants of your symbols. And finally, working with the new Rendering Modes. One note on the availability of these APIs: everything we'll see in this session is available on all Apple platforms, and look for the new badge to know when something's new in this year's releases. Let's get into the fundamentals. If you've already used Symbols from SwiftUI, this part should be a breeze.
To start, let's just show a Symbol.
The most simple way is to use one of the system-provided symbols, like "heart," in an Image. A Label view is the other main way to show symbols. A Label is a combination of a title with an icon, which is most often a symbol. The great thing about a label is that it's a general description of this pairing and will adapt its behavior to the context where it's shown. For example, in many components, the label will be shown with the symbol, and then the title. Sometimes, they'll be shown in a different layout, or sometimes, a component will show only the symbol or only the title. SwiftUI automatically makes these changes for you in built-in views, and you can use label styles in your own views to do it, too.
You can show custom symbols in both Image and Label by just changing the initializer you use. This is what each of these configurations looks like, but their visual representation isn't the only one that matters. This is how each of these views will appear to VoiceOver by default. You can see that using Label automatically gives us great Accessibility support, since the title gives a textual description of the content. Let's look closer at the image-only cases. When possible, SwiftUI will provide a label based on a system symbol's content. This might be all you need, like if you were using a heart for a "love" button. However, some symbols don't have a standard meaning, and in those cases, the default Accessibility description won't be as useful. You also might be able to add more specific information about how your app is using the symbol. You can always use an accessibilityLabel to provide that information. Our custom symbol is another case where we're not getting an ideal accessibility description. A great way to improve that is to provide a localization for the image's name in a Localizable.strings file. SwiftUI will automatically use this localizable string for the accessibility label of this image anywhere it's used.
One last way to use symbols is as part of Text. You can embed a symbol inside a run of text by using string interpolation. This is a good technique to use when you want a symbol to reflow along with your text, like this chevron.
Okay, let's go back to our Label with the heart and look at some of the modifiers we can apply to symbols to customize them. Let's start by changing our symbols' color with the foregroundStyle modifier. Text and symbols default to black or white in light mode or dark mode. You can set the foregroundStyle to a specific color, like red, or to more semantic values, like the current tint or secondary style. We'll come back to foregroundStyle again later to see some more advanced effects we can do with it.
You can use a font modifier to change both the text size, as well as the size of your symbols. If you use a text style, like body or caption, then the text and symbol will scale with dynamic type, and if you choose a fixed size, then they stay constant.
The other axis where you can change the size of symbols is their scale, using the imageScale modifier. This doesn't change the font size that's used, but how large a symbol appears relative to text.
Next, let's take a look at variants. You may have noticed that iOS tab bars should use filled symbol variants. In the past, this was something that you had to do yourself by carefully choosing the right symbol from the Symbols app, and knowing when a symbol didn't have a fill variant, like "sparkles" here. New this year, tab bars and other views now automatically opt into specific variants, like fill, to be applied to any symbols they contain. This means you can just use the base version of the symbol and get the right variant without any extra work. And by not over-specifying the exact configuration you want, you also get code that's more reusable. For example, if we run this same code on macOS, we get the correct variant for that platform: outlines.
We can use this variant support in our own components, as well. Here, I have a simple list of labels, shown with the default outline variant. To use filled variants here, I can use the new symbolVariant modifier. This modifier sets the specified variant in the environment, so it's great to use on a whole view hierarchy, like we're doing here. There's a large set of variants available: fill, slash, and a set of shape encapsulations. And these can be combined with each other, as well, the same way they're combined in the names of the symbols themselves, like .circle.fill.
This same set of variants works on your custom symbols, as well. All you have to do is follow the same naming patterns used by system symbols. Next, let's look at rendering modes. Symbols support four different rendering modes to allow you to customize the way your symbols are colored. Here are some examples of each rendering mode. We'll go over them one by one. And if you want to learn more about the design of the different rendering modes, I highly recommend "What's new in SF Symbols." Okay, let's go back to our list of cards, this time on macOS. Right now, our symbols are showing the default rendering mode, which is monochrome. This works well for showing consistent colors for a set of symbols. However, in this case, I'd like to show these symbols using the colors associated with each card. The multicolor rendering mode is a great way to show colors for what each symbol represents. And we can change to it by adding a symbolRenderingMode modifier on multicolor. If a symbol doesn't have a multicolor representation, it will fall back to the monochrome rendering mode. You can use the SF Symbols app to look up which symbols have multicolor definitions and to add multicolor support to your custom symbols. Check out the "SF Symbols App Overview" to learn more. In another part of this app, we have some buttons to shuffle cards to different parts of the deck. I'd like to emphasize the key parts of these symbols with different levels of opacity. Do you remember which rendering mode does that? That's right! Hierarchical! We can use the same symbolRenderingMode modifier with hierarchical. This uses the current foreground style to apply a single color to the symbol, like monochrome, but also adds multiple levels of opacity, to emphasize the key elements of the symbol. There's one last rendering mode, and there's a button in my app that it could help with. I'm not very good at card games, so I need this button to undo my mistakes. Here, I'm showing a symbol in a button with a .circle.fill variant to get this circular background. But the coloring isn't exactly what I want. I'd like something that better matches the color of our cards. Remember that we can use the foregroundStyle modifier to set a color on a symbol. But we can go further. New this year, we can set multiple colors to be used to draw our symbol. This uses the palette rendering mode, which allows maximum control over the coloring of the layers of a symbol. You can specify up to three styles to control each level of the symbol. This symbol has primary and tertiary content, so the first and third colors will be used. If we used a symbol that just has primary and secondary content, like the outline version of this symbol, then only those colors would be used. Because most individual symbols only use two layers, like both of these, you can specify two styles instead of three, and the last style is used for everything from secondary onwards. In many cases, this is all you need. We've been using colors so far, but this is a foreground style modifier, and it works with any ShapeStyle. We can use a secondary style to get a vibrant effect in front of blurs, or even use a material to blur the background behind a symbol. If you want to learn more about foreground styles and materials, check out "Add rich graphics to your SwiftUI app." Let's look back at the full set of rendering modes again.
The best way to see and choose which rendering modes to use is the SF Symbols app and its inspector. And these APIs to express these configurations in SwiftUI work the same way, so it's easy to go back and forth between the Symbols app and your code. You also get the best behavior with minimal configuration.
If you have only one foreground style, and don't specify a rendering mode, you'll automatically get monochrome. And if you specify more than one foreground style, but don't specify a rendering mode, you'll automatically get palette.
The Symbols app has a large set of colors available to use with these different rendering modes. And that full set of colors is available in SwiftUI, as well, including several colors and styles that are new this year. These colors are optimized for all the different configurations they appear in: light and dark mode, special rendering over blurs, even the specific platform they're shown on. We just saw how to create symbols, a set of modifiers to change their appearance, how to change which variant of a symbol is shown, choosing different rendering modes, and how to use foreground styles to customize your symbol's coloring. SF Symbols make it easy to add beautiful graphics to your app and to customize them as little or as much as you want. See where you can adopt them to make your apps look even better. Thanks for watching and have a great WWDC. [upbeat music]
-
-
0:45 - Creating Symbols
// System symbol image Image(systemName: "heart") // System symbol label Label("Heart", systemImage: "heart") // Custom symbol image Image("queen.heart") // Custom symbol label Label("Queen of Hearts", image: "queen.heart")
-
2:33 - Accessibility Label
Image(systemName: "heart") .accessibilityLabel("Ace of Hearts") Image(systemName: "person.circle") .accessibilityLabel("Profile") Image("queen.heart") // Localizeable.strings "queen.heart" = "Queen of Hearts";
-
2:59 - Symbol in Text
Text(""" Thalia, Paul, and 3 others \(Image(systemName: "chevron.forward")) """)
-
3:14 - Customizing Color
Label("Heart", systemImage: "heart") Label("Heart", systemImage: "heart") .foregroundStyle(.red) Label("Heart", systemImage: "heart") .foregroundStyle(.tint) Label("Heart", systemImage: "heart") .foregroundStyle(.secondary)
-
3:51 - Customizing Font
Label("Heart", systemImage: "heart") .font(.body) Label("Heart", systemImage: "heart") .font(.caption) Label("Heart", systemImage: "heart") .font(.system(size: 10))
-
4:08 - Customizing Scale
Label("Heart", systemImage: "heart") .imageScale(.large) Label("Heart", systemImage: "heart") .imageScale(.medium) Label("Heart", systemImage: "heart") .imageScale(.small)
-
4:23 - Customizing Variants
TabView { Text("Cards").tabItem { Label("Cards", systemImage: "rectangle.portrait.on.rectangle.portrait") } Text("Rules").tabItem { Label("Rules", systemImage: "character.book.closed") } Text("Profile").tabItem { Label("Profile", systemImage: "person.circle") } Text("Magic").tabItem { Label("Magic", systemImage: "sparkles") } }
-
5:12 - Monochrome
List { Label("Ace of Hearts", systemImage: "suit.heart") Label("Ace of Spades", systemImage: "suit.spade") Label("Ace of Diamonds", systemImage: "suit.diamond") Label("Ace of Clubs", systemImage: "suit.club") Label("Queen of Hearts", image: "queen.heart") } .symbolVariant(.fill)
-
6:41 - Multicolor
List { Label("Ace of Hearts", systemImage: "suit.heart") Label("Ace of Spades", systemImage: "suit.spade") Label("Ace of Diamonds", systemImage: "suit.diamond") Label("Ace of Clubs", systemImage: "suit.club") Label("Queen of Hearts", image: "queen.heart") } .symbolVariant(.fill) .symbolRenderingMode(.multicolor)
-
7:10 - Hierarchical Rendering Mode
HStack { Button(action: {}) { Image(systemName: "square.3.stack.3d.top.fill") } Button(action: {}) { Image(systemName: "square.3.stack.3d.bottom.fill") } } .symbolRenderingMode(.hierarchical)
-
7:50 - Palette Rendering Mode
Button(action: {}) { Image(systemName: "arrow.uturn.backward") } .symbolVariant(.circle.fill) .foregroundStyle(.white, .yellow, .red)
-
9:00 - Advanced Foreground Styles
Button(action: {}) { Image(systemName: "arrow.uturn.backward") } .symbolVariant(.circle.fill) .foregroundStyle(.white, .red) Button(action: {}) { Image(systemName: "arrow.uturn.backward") } .symbolVariant(.circle.fill) .foregroundStyle(.white, .secondary) Button(action: {}) { Image(systemName: "arrow.uturn.backward") } .symbolVariant(.circle.fill) .foregroundStyle(.red, .regularMaterial)
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.