SF Symbols Module

Hi All,
Anybody who has helped before may recognise I am a hobby programmer. I have a couple of questions if you’ve got time.

A few weeks ago I tried to have a look at a post about using SF Symbols. Can’t remember where it came from but most likely from the regular “Forum Updates” that come out. Anyway, (and I apologise to the author for not remembering) but it involved a module he created called macOSLib which then had multiple Enumerations, Methods and Structures. I totally do not understand most of the coding but I have copied the module into my projects (I hope that’s ok) and it works wonderfully but my question is, does it have to be that complicated to use SF Symbols? To insert one you have to put a line into the “Open” event of the button -
SystemImage(“clear”,18.0, SystemImageWeights.Regular,SymbolScale.Large,Me.Handle).

I hope at least some of you know the code I am talking about without having to put it all in here.

Second one, what is the purpose of #. I often see it (and it is in this code), but don’t get what it’s doing. Very hard to find reference to it in the docs. If I enter # into the search box all I get is #else, #elseif, #endif but they do expand to any explanation - hardly very helpful.

Thanks everyone.

Barry

A library such as MacOSLib contains a large and diverse number of functions and it has to be very generalized, so it can be used in a wide number of situations. In order to achieve those goals, a certain level of complexity has to be imposed. If all you needed was to use SF Symbols, yes, you could probably find a less complicated solution.

The # symbol is used to designate a compiler directive. It is used most commonly with #IF and #PRAGMA statements. The benefit of #IF is that only the code which satisfies the condition will be included in the program. The rest will be stripped out. That allows you to have code for each platform present in your code, but only the one that’s valid will be passed on to the compiler. Code that is specific to other platforms would normally cause a compile error, but by enclosing them in an #IF, the compiler never sees it, so no error.

Hi Tim,
Thanks for that. So with the # am I reading it right that if I only write to a single platform (MacOS), then I don’t have to worry about #'s?

As for the other issue, I guess implied in my question is, is there another way to use the SF Symbols? I’ve tried cutting and pasting and they don’t seem to be something that will work like that and I can’t figure out what format they are. I’m also trying to put more than one together in certain circumstances which the module I referred to won’t seem to do.

Thanks for the help tho.

This is a module I’ve build. Largely based on the blog post from Javier Menendez here:

Using that I put together this modified version that deals with a backup image a little better. It also extends the process for controls that have multiple parts like the DesktopSegmentedButton.

This is the module:
http://www.ditl.org/SystemImage.xojo_binary_code

There are two basic methods that help you use SFSymbols:

  • SystemImageControl
  • SystemImage

SystemImageControl
SystemImageControl allows you to apply an SFSymbol to a control that has one or image properties.

SystemImageControl( "chevron.left",14.0, SystemImageWeights.Regular,SymbolScale.Medium,Me.Handle, btnIconPrev )

For the segmented control you also pass in an index for the part you wish to set:

SystemImageControl( "text.alignleft", 14.0, SystemImageWeights.Medium, SymbolScale.Small, Me.Handle, segAlign_Left, 0 )
SystemImageControl( "text.aligncenter", 14.0, SystemImageWeights.Medium, SymbolScale.Small, Me.Handle, segAlign_Centre, 1 )
SystemImageControl( "text.alignright", 14.0, SystemImageWeights.Medium, SymbolScale.Small, Me.Handle, segAlign_Right, 2 )

The parameters are as follows:

  • name : The name of the SFSymbol
  • size : The font size required
  • weight : The weight of the lines
  • scale : The scale relative to other things
  • controlHandler : The handle property of the control you wish to set.
  • fallbackTemplateImage : An Xojo image you wish to use if that SFSymbol isn’t available.
  • nSegment : (optional) The index of the segment for multi segment controls.

SystemImage
The other function is SystemImage, which allows you retrieve an Xojo picture of the image containing the SFSymbol you want to use. This could be for caching purposes or if you find a problem with a control. You can also use it to save a PNG file to use on earlier versions of macOS that don’t support SFSymbols. Apple does not allow the symbols to be used on other platforms, such as Windows / Linux:

  • Pull the image using the SystemImage function
  • Save it to a png file
  • Build an Image in Xojo that you can then pass to the Control function so old and new versions of the OS look the same.

A problem seems to exist if you try and use SFSymbols on a Toolbar icons using the Control method. So for these I use the SystemImage function to retrieve the image and then set that to the toolbar. I do it using this function so that the Template image setting is on and macOS handles the toolbar colours:

Public Sub ToolbarSetIcon(itemsPtr as Ptr, index as Integer, Icon as Picture)
  Declare Function objectAtIndex Lib CocoaLib Selector "objectAtIndex:" ( theArray As Ptr, idx As Integer ) As Ptr
  Declare Sub NSControlImage Lib CocoaLib selector "setImage:" ( NSControlInstance As Ptr, Assigns inNSImage As ptr )
  Declare Sub NSImageTemplate Lib CocoaLib Selector "setTemplate:" ( NSImageInstance As ptr, Assigns value As Boolean )
  
  Var NSImage, toolbarItemPtr As Ptr
  
  toolbarItemPtr = objectAtIndex( itemsPtr, Index )
  NSImage = Icon.CopyOSHandle( Picture.HandleTypes.MacNSImage )
  NSImageTemplate( NSImage ) = True
  NSControlImage( toolbarItemPtr ) = NSImage
End Sub

Hope this helps someone

2 Likes

Compiler directives are not only to do with different platforms. For example you you choose analyse project from the project menu you will find that you get warnings. Some of these will very likely be Unused Event Parameters.

For example, If you have a DesktopLisBox control on a window and you add a CellAction event to the control. You will get two warnings one will say that you haven’t used the Row parameter and the other Column parameter.

If your listbox only has one column you likely don’t care what the value of the column parameter is. In which case you can tell the compiler that you are not going to use that parameter as follows:

Sub CellAction(row As Integer, column As Integer) Handles CellAction
    #pragma Unused column
    if row = 0 then
        ' Do something
    End if
End Sub

Now when you analyse your code it no longer warns about column not being used. There are other Pragma options:
https://documentation.xojo.com/api/language/pragma_directives.html

I forgot to say the SFSymbols module is automatically light / dark mode compatible, again because it sets the Template flag on the images.

Hi Ian,
Thanks for the reply. I have to say, like the other one, I still don’t know what it all means and why it is necessary but, that’s ok, just a hobbiest and, if I can access modules like this, it at least solves the practical side if not the understanding side :slight_smile:

1 Like

It isn’t necessary. It is down to personal choice. For example if you have a segment button for text alignment it could look like this, and everyone would know what it was for:

Screenshot 2022-03-13 at 11.21.08

If you use SFSymbols you can have this instead:

Screenshot 2022-03-13 at 11.09.25

Everyone would still know what it was and you wouldn’t have to translate it for other languages. The choice is yours. If you choose to go with the second option you have two choices.

  1. Draw your own graphics for each part and add them as pictures to the control.
  2. Use a module to add SFSymbols to the control without the need to make your own.
SystemImageControl( "text.alignleft", 14.0, SystemImageWeights.Medium, SymbolScale.Small, Me.Handle, segAlign_Left, 0 )
SystemImageControl( "text.aligncenter", 14.0, SystemImageWeights.Medium, SymbolScale.Small, Me.Handle, segAlign_Centre, 1 )
SystemImageControl( "text.alignright", 14.0, SystemImageWeights.Medium, SymbolScale.Small, Me.Handle, segAlign_Right, 2 )

text.alignleft, text.aligncenter and text.alignright are the apple names for those symbols.

segAlign_Left etc are images I’ve exported to PNG and added to my project for earlier versions of macOS to show when the SFSymbol API doesn’t exist.

Using your own symbols gives you full control over the look and feel of your application, but requires more work on your part. It also requires you to make light mode and dark mode versions of all of these graphics.

SFSymbols allow you to make your app consistent with other apps on the system. Which in turn makes the user more familiar with it from prior experience. Obviously that only works if you use the correct symbols for the correct action. Using my module and the template flag on the graphics means that light / dark mode is handled without any code what so ever. It just works.

If you download the SF Symbol application from apple you can see the range of Symbols available:

I forgot a part of using the toolbar icons. This is the function that is used to call ToolbarSetIcon:

Public Sub ToolbarSetIcons()

  Declare Function tbar Lib CocoaLib Selector "toolbar" ( NSWindow As Ptr ) As Ptr
  Declare Function items Lib CocoaLib Selector "items" ( NSToolbar As Ptr ) As Ptr
  
  Var toolbarPtr As Ptr = tbar( Ptr( Handle ) )
  Var itemsPtr As Ptr = items( toolbarPtr )
  
  ToolbarSetIcon( itemsPtr, 0, TBNewImage )
  ToolbarSetIcon( itemsPtr, 1, TBOpenImage )
  ToolbarSetIcon( itemsPtr, 2, TBSaveImage )

End Sub

TBNewImage, TBOpenImage etc are the graphics previously retrieved using a SystemImage call. This retrieves the Toolbar and then sets the first three icons as specified.

Example project as requested:
http://www.ditl.org/SFSymbols.xojo_binary_project

It contains example buttons and segment control as well as toolbar items.
It doesn’t not contain any fallback images but the empty templates are there so the variables exist. They would need to be populated if you want the program to work on systems prior to Catalina.

1 Like

Hi Ian,
I think you misunderstood my statement “if it’s necessary”, I don’t mean that using them is necessary, I meant that if all that coding is necessary to achieve it. I really do appreciate your help and suggestions tho :slight_smile:

I’m afraid there is no other way. At first you try copying the symbols from the app and pasting them into the captions on the buttons. It all works! Then you try your app on another computer, that doesn’t have the SF Symbols app installed, and it all falls apart.

It only every works correctly when you use the correct API. The API also allows you to provide a fallback image for when the API, or the symbol you’ve chosen, isn’t available.

Even when it is supported natively by Xojo you will need to do something like this. These symbols aren’t just new letters available in a font.

I would advise you to also look into the built-in macOS images, as these span more versions than just 11 and 12.

Aqua Swatch (which is FREE) will list official published Apple icons as well a boatload more that I’ve discovered while spelunking various macOS frameworks, where possible I also list which OS versions they’re available with. For example text alignment icons have been present in the system since 10.14.

Hi Sam,
Thanks for that. I’ve downloaded Aqua Swatch and have to be honest (maybe I’m just too old to catch on to some of this) but I don’t have a clue how to make use of the images that are displayed.

On the other hand, I downloaded Iconographer Mini. That looks like fun and more intuitive to use so I’ll have a play with that.

Barry

Aqua Swatch is a reference application, it allows you the developer to see what the actual colors, icons, font settings and vibrancy look like, as opposed to Apple’s documentation which just gives you the names. You can also run this macOS 10.13 or newer to see how the icons are different across different OS versions.

To use the icons with Xojo, you’ll need either a 3rd Party collection (such as my own Ohanaware App Kit, the MBS plugin) or a bunch of declares. As Ian’s code is close, with a little more work, his code should be able to allow you to use these icons within your application also.

Typically you don’t rasterize system icons unless you want the icons to be consist across all supported OS versions or the icon you want isn’t available in a supported OS version. i.e. App Wrapper not only features a tool for rasterizing SF Symbols (and including them within your application), but also includes a whole bunch within as App Wrapper supports 10.13 and newer.

1 Like

I’ve now made this project available via GitHub.com:

3 Likes

I think the MacOSLib OP is talking about is actually the one Xavier posted as Xojo blog, and it addresses only SF Symbols.
Since I’m a happy user of both, that is the old glorious MacOSLib and Xavier’s MacOSLib (for SF Symbols), I changed the name of Xavier’s MacOSLib into something else.

My module above is an expansion of the code that appeared on the Xojo Blog about using SF Symbols in macOS by Javier Menendez. Mine alters some of the things to do with Template flag (auto light / dark mode operation) and better deals with using fallback images. It also adds support for controls that support more than one image (such as the segment button). The original only supported ones that had one image property.

1 Like

Ian
This is really useful - thanks.

Any idea why the picture resulting from SystemImage(…) should always be double the height and double the width of the resulting icon? See image below which shows the picture size around each icon:

No, I’ve not come across that. What parameters are you using? If it truly is double then retina scaling comes to mind?