How To Use Fonts As Images In Xamarin Forms (Part 1/2)
Icons – The Standard Way
There are so many little details that a software developer must take care of before publishing an application. One of the most time-consuming is the task of adding icons to your toolbars, buttons, menus, headers, footers and so on. It’s not just the effort of finding or creating the right images, or as in the case of multiplatform applications such as Xamarin, using the right visual metaphor for each target platform; it’s a matter of scaling all of the assets to create properly sized images that fit @1x, @2x, @3x, hdpi, xhdpi, xxhdi, and so on. Assuming that you get all of the source images right the first time in a moderately complex application, you could end up with hundreds of assets to wrangle. Then if your designer (assuming that you have one on the project) changes a couple of icons, you will have dozens of assets to individually remove and replace . If you’ve done this before, you know how time-consuming and error-prone it is.
There Must Be A Better Way
There is a better way and it is surprisingly simple. Instead of using individually rendered and scaled bitmap images in the form of .png files, use True Type Fonts. You can use them in any control that can display text, including Buttons, Labels, Pickers controls that can display text through ItemTemplates.
Here are a few reason why you want to do this:
- Vector Representation: .png and .jpg files are stored as Raster images. Essentially, they are just bitmaps or pixel by pixel representations. When you scale them up to larger sizes, the individual pixels stored in the files scale up as well. The result is at best, a fuzzy image with soft edges. At worst, your users could be present with a higly pixelated images. Vector based fonts such as TTF do not contain pixel definitions. Instead, the outlines of the characters (or glyphs) in TrueType fonts are made of straight line segments and quadratic Bézier curves. This makes the characters directly scalable (up and down) with no degrataion in the quality of the rendered image. Icon font scaling is done simply by setting the the FontSize property.
- Lightweight: Hundreds of icons can be stored in a single font file with a size of only a few KB, instead of a few KB per single .png or .jpg image.
- Colours: You do not need to determine colours of your icons before you create your image file. The with vector based font files, there is no inherent colour to the characters; you can render them and change the colour in your code at runtime by changing the Foreground colour. Be aware, however, that you cannot have a single icon image that contains more than one colour.
- Consistent Metaphors: They say that a picture is worth a thousand words and that is true if the picture is understood. When delivering an application that must be understandable across platforms, languages and cultures, selecting the right visual methaphors for your application can be daunting and time-consuming. Whether or not you are a fan of Google’s Material Design Language, it brings to your app a very large set of visual metaphors (icons) that are already widely recognized and understood by a great many people. If you use these icons wisely, your users will learn how to use your app and be comfortable much more quickly than if you introduce new visual metaphors that your users will have to learn.
The Basics
Get The Font File
To use font as images approach, you need a .ttf font file. For our example, we will use the Material Design Icons. You can use any .ttf file you wish, but the Material Design Icons .ttf file contain, within a single file, many, or most of the icons that you might want to use in your app. Here is an index of the ever-growing list of icons in the collection. The site includes detailed instructions for adding the font file to your Xamarin Forms application.
If you want to create your own custom .ttf icon files, there are tools such as SyncFusion Metro Studio that can generate .ttf files for you based on characters that you gather together from other source files such as Windows WingDings.
Install The Font File
Each platform stores and accesses their resources differently. Putting everything in the correct location is essential for this to work, so we have to be precise. I have always found storing and access assets to be one of the more confusing things about working with Xamarin Forms, so I am providing all of the details here.
- Android: Add it to the Assets folder. Mark it as an AndroidAsset
- iOS: Add the font file with Build Action: BundleResource, and update the Info.plist file (Fonts provided by application, or UIAppFonts, key).
Things are rarely easy when dealing with iOS. To make the font file accessible on iOS, you will have to add the following to your info.plist file. Open the file by right clicking on it in the Solution Explorer and selecting Open With > XML (Text) Editor
<key>UIAppFonts</key>
<array>
<string>materialdesignicons-webfont.ttf</string>
</array>
- UWP: Add the file to /Assets/Fonts/ and make sure it is marked as Content. If there is not already a /Fonts folder, then add one.
Accessing The Fonts
Here is where things get a bit tricky. The best way to approach sharing the font file across projects is to create a Resource Dictionary in the App.xaml file in the Shared Project. In this case, it is the FontsAsImages project in our solution. We use the OnPlatform key to point to the appropriate location for each platform. Take careful note of the naming and syntax used for accessing the font file on each respective platform. Material Design Icons is the name of the font inside of the font file. Notices that the iOS entry does not contain the file name. That’s becuase the plist entry that we made above, provides the file reference.
<ResourceDictionary>
<OnPlatform x:Key="MaterialDesignIcons" x:TypeArguments="x:String" >
<On Platform="Android"
Value="materialdesignicons-webfont.ttf#Material Design Icons" />
<On Platform="iOS"
Value="Material Design Icons" />
<On Platform="UWP"
Value="/Assets/Fonts/materialdesignicons-webfont.ttf#Material Design Icons" />
</OnPlatform>
</ResourceDictionary>
Using The Fonts In Your App
Now that the font has been installed and made accessible, you can start using it in your app. To access the individual symbols, you need to use the Unicode value of the desired element. In our first example, we will use the Unicode value of f845. It will render a Xamarin logo for us. Yes, there is a Material character for Xamarin. To do so, however, we need to use the appropriate escape characters to ensure that the Unicode value is not interpreted literally. In the following example, notice the prefix of &#x and the postfix of ; Combined with a FontFamily property, they tell Xamarin to interpret the value as a character from the FontFamily, indexed by its Unicode value of f845.
<Label Text=""/>
Using The Font File
To save you a bit of time, here are a couple of terrific resources for making sense of the contents of the font file. With both of these tools, you upload the .ttf file. The online tools parse the .ttf file and provide you with a view into the file, including previewing the images.
CharacterMap by Mathew Kurian
CharactherMap provides a very useful view into the .ttf file, including a vew of the actual vector maps. Its a great tool, but it does not display the name of the charcter with the thumbnails. It also shows the index as an integer value instead of the hex value that we need in our XAML, so locating a character by its hex value is a bit tedious. Otherwise, it’s a great tool.
- Online character mapping: http://bluejamesbond.github.io/CharacterMap/
- GitHub Repo: https://github.com/bluejamesbond/CharacterMap
IconFont2Code by Andrei Nitescu
This tool provides thumbnails with names and the ability to convert the glyph codes to C# code which we will use in part 2 of this article.
- Online tool: https://andreinitescu.github.io/IconFont2Code/
- Andrei’s blog: https://enginecore.blogspot.com/
The Demo App
Here is what we are going to build:
With the font file installed, plist changes implemented and the ResourceDictionary added to your App.xaml file in your shared project, you are ready to go. Here is the code for the enitre demo page. It’s pretty self-explanatory, so I am not going to go over the details. It displays a row of text that is the title of the page, followed by a large Xamarin icon and then a row of buttons such as you might find on a media player. There is also a Style for the buttons. I have set the colour of the icon to green for the Play button, simply to show how easy it is to do.
I will soon upload the code to Github.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="FontsAsImages.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="Button_MediaPlayerStyle" TargetType="Button">
<!--
NOTE: Do not use
<Setter Property="FontAttributes" Value="Bold"/>
It will render entirely different glyphs on iOS.
-->
<Setter Property="FontFamily" Value="{StaticResource MaterialDesignIcons}"/>
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="VerticalOptions" Value="Center"/>
<Setter Property="HorizontalOptions" Value="Center"/>
<Setter Property="WidthRequest" Value="50"/>
<Setter Property="FontSize" Value="48"/>
<Setter Property="TextColor" Value="Black"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ReourceDictionary>
</ContentPage.Resources>
<StackLayout
HorizontalOptions="CenterAndExpand"
VerticalOptions="Start">
<Label Text="Use Fonts For Icons!"
HorizontalOptions="Center"
VerticalOptions="Start"
Margin="0,128,0,0"/>
<Label
Text=""
FontFamily="{StaticResource MaterialDesignIcons}"
BackgroundColor="Transparent"
VerticalOptions="Start"
HorizontalOptions="Center"
WidthRequest="128"
HeightRequest="128"
FontSize="128"
TextColor="#3598DB"
Padding="0"/>
<StackLayout
Orientation="Horizontal"
HorizontalOptions="Center"
VerticalOptions="Start">
<Button x:Name="Button_Rewind"
Text=""
Style="{StaticResource Button_MediaPlayerStyle}"/>
<Button x:Name="Button_Stop"
Text=""
Style="{StaticResource Button_MediaPlayerStyle}"/>
<Button x:Name="Button_Pause"
Text=""
Style="{StaticResource Button_MediaPlayerStyle}"/>
<Button x:Name="Button_Play"
Text=""
Style="{StaticResource Button_MediaPlayerStyle}"
TextColor="Green"/>
<Button x:Name="Button_FastForward"
Text=""
Style="{StaticResource Button_MediaPlayerStyle}"/>
</StackLayout>
</StackLayout>
</ContentPage>
That pretty much covers it.
In part 2 of this article, I will cover some more advanced things including using constants to represent your glyph codes and databinding your font definitions.
I look forward to your comments.
Michael
I am an independent consultant who has been leading software teams, designing, building and delivering software for nearly three decades. It’s still as exciting and enjoyable for me today as at was when I wrote my very first Hello World program and saw it spring to life in front of me.
Leave a Reply