Earlier this year ago I wrote a post about using the SF Font symbols on macOS Picture.SystemImagein iOS apps. However that technique has some downsides. For one, the symbol glyphs are hardcoded, which means that it’s not possible to access the new symbols added to the SF Font by Apple. In addition, it isn’t possible to set the font weight and scale for the glyph. In this new post, I’ll show a more flexible way to work with these symbols on macOS 11+.
This technique is more on par with the current Picture.SystemImage you use in your Xojo iOS apps. With it you will be able to set the following attributes:
- Glyph name. You can use Apple’s Symbols SF utility to view all the available symbols and the name associated with each of these.
- Size (in points)
- Weight. The weight of the SF Font, ranging from Ultra-Light to Black. This is an Enumeration value.
- Scale. The scale of the SF Font symbol, ranging from Small to Large. This is an Enumeration value.
- TemplateColor. If you want to tint the resulting glyph, you can provide a ColorGroup parameter (Desktop apps) or a regular Color parameter (Desktop, Console apps).
- FallbackTemplateImage. You can provide a grayscale / black and white image to use as a fallback image, in case there is no way to create a picture from the received “name” string.

As it is the case with the existing iOS method, you will receive a Picture. For example you will be able to use code like this in your desktop projects:
Var c As New ColorGroup(Color.Red, Color.White)
Var p As Picture = SystemImage("highlighter", 120.0, SystemImageWeights.UltraLight, Symbolscale.Small, c, fallback)
IVSFGlyph.Image = p
The “fallback” parameter refers to an image added to a desktop project that will be used if the glyph is not found. This will produce the following glyph, assigned as the image to the ImageView control named IVSFGlyph:

In order to add this kind of functionality to your Xojo Desktop and Console projects on macOS, add a new Module to the Navigator (for example, with the name macOSLib). Then, with the macOSLib item selected in the Navigator, add two enumerations with the following values:
- Name: SymbolScale
- Type: Integer
- Scope: Global
- Values:
Small = 1
Medium = 2
Large = 3
- Name: SystemImageWeights
- Type: Integer
- Values:
UltraLight = 0
Thin = 1
Light = 2
Regular = 3
Medium = 4
Semibold = 5
Bold = 6
Heavy = 7
Black = 8
Now add to the macOSLib module the method that will run both on Desktop and Console macOS apps:
- Method Name: SystemImage
- Parameters: name As String, size As Double, weight As SystemImageWeights = SystemImageWeights.Regular, scale As SymbolScale = SymbolScale.Medium, templateColor As Color, fallbackTemplateImage As Picture = Nil
- Return Type: Picture
- Scope: Global
Click on the cog wheel icon in the Inspector to change to the attributes section for the method, and make sure to set only the options displayed in the following picture:

Finally, put the following code in the Code Editor associated with the newly created method:
#If TargetMacOS
If System.Version >= "11.0" Then
If name = "" Then Return Nil
Declare Function Alloc Lib "Foundation" Selector "alloc" (classRef As Ptr) As Ptr
Declare Sub AutoRelease Lib "Foundation" Selector "autorelease" (classInstance As Ptr)
Declare Function NSClassFromString Lib "Foundation" (clsName As CFStringRef) As Ptr
Declare Function ImageWithSystemSymbolName Lib "AppKit" Selector "imageWithSystemSymbolName:accessibilityDescription:" (imgClass As Ptr, symbolName As CFStringRef, accesibility As CFStringRef) As Ptr
Declare Function ConfigurationWithPointSize Lib "AppKit" Selector "configurationWithPointSize:weight:scale:" (symbolConfClass As Ptr, size As CGFloat, weight As CGFloat, tscale As SymbolScale) As Ptr
Declare Function ImageWithSymbolConfiguration Lib "AppKit" Selector "imageWithSymbolConfiguration:" (imgClass As Ptr, config As Ptr) As Ptr
Declare Function ColorWithRGBA Lib "Foundation" Selector "colorWithRed:green:blue:alpha:" (nscolor As ptr, red As CGFloat, green As CGFloat, blue As CGFloat, alpha As CGFloat) As Ptr
Declare Sub SetTemplate Lib "AppKit" Selector "setTemplate:" (imageObj As Ptr, value As Boolean)
Declare Sub LockFocus Lib "AppKit" Selector "lockFocus" (imageObj As Ptr)
Declare Sub UnlockFocus Lib "AppKit" Selector "unlockFocus" (imageObj As Ptr)
Declare Sub Set Lib "Foundation" Selector "set" (colorObj As Ptr)
Declare Sub NSRectFillUsingOperation Lib "AppKit" (rect As NSRect, option As UInteger)
Declare Function RepresentationUsingType Lib "AppKit" Selector "representationUsingType:properties:" (imageRep As Ptr, type As UInteger, properties As Ptr) As Ptr
Declare Function InitWithFocusedView Lib "AppKit" Selector "initWithFocusedViewRect:" (imageObj As Ptr, rect As NSRect) As Ptr
Var nsimage As Ptr = NSClassFromString("NSImage")
Var orImage As Ptr = ImageWithSystemSymbolName(nsimage, name, "")
Var symbolConfClass As Ptr = NSClassFromString("NSImageSymbolConfiguration")
// Getting the weight as the required SystemImageWeight float
Var tWeight As CGFloat = SystemImageWeight(weight)
// Creating a configuration obj for the Glyph
Var symbolConf As Ptr = ConfigurationWithPointSize(symbolConfClass, size, tWeight, scale)
// Getting the final NSImage from the Glyph + Conf (still in vectorial format)
Var finalImage As Ptr = ImageWithSymbolConfiguration(orImage, symbolConf)
// Can't create image from received glyph name, so we return Nil if fallback is not provided
// or colorize the fallback image if there is one
If finalImage = Nil Then
If fallbackTemplateImage = Nil Then Return Nil
Var fallbackData As MemoryBlock = fallbackTemplateImage.ToData(Picture.Formats.PNG)
Var fallbackDataPtr As Ptr = fallbackData
Declare Function DataWithBytesLength Lib "Foundation" Selector "dataWithBytes:length:" (dataClass As Ptr, data As Ptr, length As UInteger) As Ptr
If fallbackData <> Nil And fallbackData.Size > 0 Then
Var NSDataClass As Ptr = NSClassFromString("NSData")
Var NSDataObj As Ptr = DataWithBytesLength(NSDataclass, fallbackDataPtr, fallbackData.Size)
If NSDataObj <> Nil Then
Declare Function InitWithData Lib "AppKit" Selector "initWithData:" (imageInstance As Ptr, data As Ptr) As Ptr
Var NSImageClass As Ptr = NSClassFromString("NSImage")
finalImage = Alloc(NSImageClass)
finalImage = InitWithData(finalImage, NSDataObj)
AutoRelease(NSDataObj)
End If
End If
End If
If finalImage = Nil Then Return Nil
Var c As Color
Var nscolor As Ptr
LockFocus(finalImage)
// Applying tint to the image if we receive a valid ColorGroup object
c = templateColor
nscolor = NSClassFromString("NSColor")
Var tColor As Ptr = ColorWithRGBA(nscolor, c.Red/255.0, c.Green/255.0, c.Blue/255.0, 1.0-c.Alpha/255.0)
// We need to set the Template property of the NSImage to False in order to colorize it.
SetTemplate(finalImage, False)
Declare Function ImageSize Lib "AppKit" Selector "size" (imageObjt As Ptr) As NSSize
Var tRect As NSRect
tRect.Origin.X = 0
tRect.Origin.Y = 0
tRect.RectSize = ImageSize(finalImage)
Set(tColor)
NSRectFillUsingOperation(tRect, 3)
// Getting bitmap image representation in order to extract the data as PNG.
Var NSBitmapImageRepClass As Ptr = NSClassFromString("NSBitmapImageRep")
Var NSBitmapImageRepInstance As Ptr = Alloc(NSBitmapImageRepClass)
Var newRep As Ptr = InitWithFocusedView(NSBitmapImageRepInstance, tRect)
UnlockFocus(finalImage)
Var data As Ptr = RepresentationUsingType(newRep, 4, Nil) // 4 = PNG
AutoRelease(newRep)
AutoRelease(nscolor)
// Getting image data to generate the Picture object in the Xojo side
Declare Function DataLength Lib "Foundation" Selector "length" (obj As Ptr) As Integer
Declare Sub GetDataBytes Lib "Foundation" Selector "getBytes:length:" (obj As Ptr, buff As ptr, len As Integer)
// We need to get the length of the raw data…
Var dlen As Integer = DataLength(data)
// …in order to create a memoryblock with the right size
Var mb As New MemoryBlock(dlen)
Var mbPtr As Ptr = mb
// And now we can dump the PNG data from the NSDATA objecto to the memoryblock
GetDataBytes(data, mbPtr, dlen)
// In order to create a Xojo Picture from it
Return Picture.FromData(mb)
End If
#EndIf
As you can see, this method calls the SystemImageWeight method, and also makes use of some Structs. Let’s add to the method that will convert the received enum value to the required CGFloat value needed by the Declare:
- Method Name: SystemImageWeight
- Parameters: weight As SystemImageWeights
- Returned Type: CGFloat
- Scope: Global
Add the following code in the associated Code Editor:
Var tWeight As CGFloat = 0 Select Case weight Case SystemImageWeights.UltraLight tWeight = -1.0 Case SystemImageWeights.Thin tWeight = -0.75 Case SystemImageWeights.Light tWeight = -0.5 Case SystemImageWeights.Regular tWeight = -0.25 Case SystemImageWeights.Medium tWeight = 0 Case SystemImageWeights.Semibold tWeight = 0.25 Case SystemImageWeights.Bold tWeight = 0.5 Case SystemImageWeights.Heavy tWeight = 0.75 Case SystemImageWeights.Black tWeight = 1 End Select Return tWeight
And now add three new Structures to the macOSLib module using the following values:
- Structure Name: NSOrigin
- Scope: Global
X as CGFloat
Y as CGFloat
- Structure Name: NSSize
- Scope: Global
Height as CGFloat
Width as CGFloat
- Structure Name: NSRect
- Scope: Global
Origin as NSOrigin
RectSize as NSSize
Add an overloaded method only intended for macOS desktop apps. The main difference is that this one can receive a ColorGroup parameter instead of just a Color object:
- Method Name: SystemImage
- Parameters: name As String, size As Double, weight As SystemImageWeights = SystemImageWeights.Regular, scale As SymbolScale = SymbolScale.Medium, templateColor As ColorGroup = Nil, fallbackTemplateImage As Picture = Nil
- Returned Type: Picture
- Scope: Global
And type the following code in the associated Code Editor for the method:
Var c As Color If templateColor <> Nil Then c = templateColor End If Return SystemImage(name, size, weight, scale, c, fallbackTemplateImage)
Click on the cog wheel icon from the method Inspector and set the attributes as shown in the following picture:

Setting SF Glyphs directly to Views
Let’s add now a third method to the module whose main difference is that it will not return a Picture; instead, it will set the specified SF glyph as the image for the received control handler. For example, this is neat if you want to set a SF symbol to a button in you UI interface (among other UI controls).

This is the signature for the new method:
- Name: SystemImage
- Parameters: name As String, size As Double, weight As SystemImageWeights = SystemImageWeights.Regular, scale As SymbolScale = SymbolScale.Small, controlHandler As Integer
- Scope: Global
And the code to put in the associated Code Editor:
#If TargetMacOS
If System.Version >= "11.0" Then
If name = "" Then Exit
Declare Function Alloc Lib "Foundation" Selector "alloc" (classRef As Ptr) As Ptr
Declare Sub AutoRelease Lib "Foundation" Selector "autorelease" (classInstance As Ptr)
Declare Function NSClassFromString Lib "Foundation" (clsName As CFStringRef) As Ptr
Declare Function ImageWithSystemSymbolName Lib "AppKit" Selector "imageWithSystemSymbolName:accessibilityDescription:" (imgClass As Ptr, symbolName As CFStringRef, accesibility As CFStringRef) As Ptr
Declare Function ConfigurationWithPointSize Lib "AppKit" Selector "configurationWithPointSize:weight:scale:" (symbolConfClass As Ptr, size As CGFloat, weight As CGFloat, scale As SymbolScale) As Ptr
Declare Function ImageWithSymbolConfiguration Lib "AppKit" Selector "imageWithSymbolConfiguration:" (imgClass As Ptr, config As Ptr) As Ptr
Var nsimage As Ptr = NSClassFromString("NSImage")
Var orImage As Ptr = ImageWithSystemSymbolName(nsimage, name,"")
Var symbolConfClass As Ptr = NSClassFromString("NSImageSymbolConfiguration")
// Getting the weight as the required SystemImageWeight float
Var tWeight As CGFloat = SystemImageWeight(weight)
// Creating a configuration obj for the Glyph
Var symbolConf As Ptr = ConfigurationWithPointSize(symbolConfClass, size, tWeight, scale)
// Getting the final NSImage from the Glyph + Conf (still in vectorial format)
Var finalImage As Ptr = ImageWithSymbolConfiguration(orImage, symbolConf)
// We need to know if the received Handler can respond to the setImage message (that is, it's a View)
Declare Function RespondsToSelector Lib "/usr/lib/libobjc.A.dylib" Selector "respondsToSelector:" (obj As Integer, sel As Ptr) As Boolean
Declare Function NSSelectorFromString Lib "Foundation" (sel As CFStringRef) As Ptr
Var sel As Ptr = NSSelectorFromString("setImage:")
// We check if it's a valid handler, we have an NSImage object and the handler can receive the "setImage" message
If controlHandler <> 0 And finalImage <> Nil And RespondsToSelector(controlHandler, sel) Then
Declare Sub Set Lib "AppKit" Selector "setImage:" (control As Integer, Image As Ptr)
// We set the NSImage to the received control
Set(controlHandler, finalImage)
End If
End If
#EndIf
So, for example, you can set now the Gear symbol as the image of a PushButton control added to the app UI using the following code in the Open Event Handler of the PushButton instance:
SystemImage("gearshape.2", 14.0, SystemImageWeights.Regular, SymbolScale.Small, Me.Handle)
Summary
And that’s all! As you can see, there are no hardcoded codepoints for the SF glyphs. You’ll be able to create a picture from any SF symbol at the desired points size, weight and scale. Plus, you’ll be able to colorize it and receive a fallback image if anything goes wrong in the process.
You can download the example Xojo project with the Module already created from this link.
