Create Custom Symbols with Multicolour Support for XCode

While developing an iOS / MacOS app in Swift recently, we had the requirement for a few symbols beyond what is available in SF Symbols. After trying unsuccessfully to create our own, we came across a fantastic Mac app Create Custom Symbols. This app very easily takes an svg created in your favourite drawing tool (shout out to Affinity Designer 2) and creates a custom symbol file compatible with Xcode.

I noticed that the web page for Create Custom Symbols notes that it has no support for multicolor symbols, but it turns out that it isn’t necessary for multicolor support to be embedded by Create Custom Symbols because it is very easy to add that support using the SF Symbols app.

*TLDR: create a custom symbol using Create Custom Symbols, drag that symbol file into SF Symbols, create your layers, export the symbol from there, and then add to your project in Xcode.

Read on for details on how to do this…

Create & save your svg

Step 1

Create your image in your drawing tool and save it as an svg.

Step 2

In the Create Custom Symbols app, create a new symbol using your svg, and export it as a symbol.
Create a new custom symbol using your svg
Drag your exported custom symbol into SF Symbols

Step 3

  • Drag the exported symbol file into the Custom Symbols 1 section of SF Symbols.
  • Choose a style from the Rendering drop-down 2. You will likely want to configure your symbol for all the styles.
  • Note the Layers section 3 in the lower right – that is where you will be mostly working in order to add support for the various renderings, including multicolor.

Step 4

  • By default, your symbol will likely have 1 layer when you add it to SF Symbols. If you expand that layer, you will see many subpaths 2, depending on the complexity of your svg. In this example, the image is made of 4 simple shapes, so there are 4 subpaths, one for each shape. Your image may have more or less.
  • In order to add support for other renderings, you will need to create new layers by clicking the + button below the layers section 1. Create a layer for each subpath that you want to render differently. In this example, I want to control the rendering of each shape in the image, so I have created 4 layers 3.
  • For the monochrome rendering, you can set it to draw or erase a layer 3. For this example, I am alternating those options to make each layer appear the in the way I would expect.
  • Inside each layer, select the subpath that is relevant for that layer. The order in which you set them makes a difference, so you may need to experiment a bit. In general, each shape you want to appear ‘behind’ the one above it, needs to be lower down in the layers list. In this example, the star needs to be the top layer in order for it to appear. I have expanded the first (top) layer, and deselected all subpaths except the star. 4
  • Whereas for monochrome you can choose to draw or erase, for each of the other renderings – hierarchical, palette, and multicolor, there are more options as to how to treat each layer. See Step 6 for more information about how to use the rendering modes in Xcode.
Create your layers in SF Symbols
Export your symbol and add it to your project in Xcode

Step 5

  • When you have your layers all set up the way you like, and each element of your symbol is visible and correct in all the renderings, select File | Export Symbol from the menu.

    Tip: Be sure to export the symbol, not the template.

  • In Xcode, click on Assets.xcassets and create a new Symbol Image Set. Drop your exported symbol into the slot provided.

Step 6

Renderings – Xcode styling

Monochrome

From Apple: A mode that renders symbols as a single layer filled with the foreground style.


VStack{
  Image("CustomSymbol")
      .symbolRenderingMode(.monochrome)
      .font(.system(size: 120))
  Text(".monochrome")
      .font(.caption2)
}
VStack{
  Image("CustomSymbol")
      .symbolRenderingMode(.monochrome)
      .font(.system(size: 120))
      .foregroundStyle(.accent)
      //accentColor is set to orange in Assets
  Text(".monochrome accent")
      .font(.caption2)
}

Note that when using custom symbols, the systemName: property of Image() is not used – just reference the symbol name as a string as shown in the example above.

Rendering: .monochrome
Rendering: .hierarchical
Hierarchical

From Apple: A mode that renders symbols as multiple layers, with different opacities applied to the foreground style.


VStack{
  Image("CustomSymbol")
    .symbolRenderingMode(.hierarchical)
    .font(.system(size: 120))
  Text(".hierarchical")
    .font(.caption2)
}
VStack{
  Image("CustomSymbol")
    .symbolRenderingMode(.hierarchical)
    .font(.system(size: 120))
    .foregroundStyle(.accent)
     //accentColor is set to orange in Assets
  Text(".hierarchical accent")
    .font(.caption2)
}
Palette

From Apple: A mode that renders symbols as multiple layers, with different styles applied to the layers.

When using .palette you can explicitly define what colours to use for each layer by using the .foregroundColor modifier with 2 or 3 colors, making it truly multicolour.


VStack{
  Image("CustomSymbol")
    .symbolRenderingMode(.palette)
    .foregroundStyle(.primary, .blue, .yellow)
    //symbolRenderingMode can be omitted, as specifying
    //multiple foreground styles implies switching to
    //palette rendering mode
    .font(.system(size: 120))
  Text(".palette (.primary, .blue, .yellow)")
    .font(.caption2)
}
VStack{
  Image("CustomSymbol")
    .symbolRenderingMode(.palette)
    .font(.system(size: 120))
    .foregroundStyle(.blue, .accent, .primary)
  Text(".palette (.blue, .accent, .primary)")
    .font(.caption2)
}
Rendering: .palette
Rendering: .multicolor
Multicolor

From Apple: A mode that renders symbols as multiple layers with their inherit styles. The layers may be filled with their own inherent styles, or the foreground style.

Use the .foregroundColor modifier with 1 color, which affects the top layer.


VStack{
  Image("CustomSymbol")
    .symbolRenderingMode(.multicolor)
    .font(.system(size: 120))
  Text(".multicolor")
    .font(.caption2)
}
VStack{
  Image("CustomSymbol")
    .symbolRenderingMode(.multicolor)
    .font(.system(size: 120))
    .foregroundStyle(.blue)
  Text(".multicolor (.blue)")
    .font(.caption2)
}
VStack{
  Image("CustomSymbol")
    .symbolRenderingMode(.multicolor)
    .font(.system(size: 120))
    .foregroundStyle(.accent)
  Text(".multicolor (.accent)")
    .font(.caption2)
}