Xamarin.Forms - convenient use of icon fonts in the application







Formulation of the problem



You can use images in various formats, for example png, svg or ttf fonts, to display icons in the Xamarin.Forms application. Most often, a font with icons, for example google material icons, is convenient for adding standard icons. The font with icons has a size of about 200K and usability is usually more important than saving on the size of the application. Icons will look good at any screen resolution and will be black and white.



There are ready-made nuget packages for using icons. I've been using iconize for a long time (nuget - www.nuget.org/packages/Xam.Plugin.Iconize ; git - github.com/jsmarcus/Iconize ). It allows you to connect more than ten fonts, adds new controls, such as IconButton, IconImage, IconLabel, etc. But here there are the usual arguments against ready-made libraries: extra functionality, extra file size, not completely satisfied with behavior, bugs, etc. Therefore, at some point I decided to abandon the finished library and replace it with a simple bike from a pair of classes + font.



The icon from the ttf font is used in xaml as follows (iOS):



<Label Text="&#xe5d2;" FontFamily="MaterialIcons-Regular"/>
      
      





In C #:



 var label = new Label { Text ="\ue5d2", FontFamily = "MaterialIcons-Regular" };
      
      





I will list the problems that arise.



1. different format for writing the icon code in XAML and C # ("& # xe5d2;" / "\ ue5d2")



2. the icon code is not associated with the icon itself, I would like to use something like “arrow_back”, as in iconize



3. if the application will run on android and iOS, then you need to write the font name differently - “MaterialIcons-Regular.ttf # MaterialIcons-Regular” in android and “MaterialIcons-Regular” in iOS



The solution described here requires a TextChanged event for the Label control. Add this problem to the list:



4. the Label control does not have a TextChanged event. We need this to track label text changes and use binding in xaml



Decision



For example, take the font google material icons. First, add it to android and iOS projects.

Download font here

Icons search here



Download the font file - MaterialIcons-Regular.ttf.



Android:

Add MaterialIcons-Regular.ttf to the Assets folder and select the “Build action” AndroidAsset for it.



iOS:

Add MaterialIcons-Regular.ttf to the Resources \ Fonts folder and select the “Build action” BundleResource for it, then write the font in info.plist:



 <key>UIAppFonts</key> <array> <string>Fonts/MaterialIcons-Regular.ttf</string> </array>
      
      





Now suppose we want a left arrow icon in our application. We go to the page with the icon search engine: https://material.io/resources/icons/ , enter "arrow" in the upper left corner, select the left arrow in the results. Now click on the “Selected Icon” panel to see the icon code ... but it’s not there:



 Usage: <!--For modern browsers--> <i class="material-icons"> arrow_back </i> <!-- For IE9 or below. --> <i class="material-icons"> arrow_back </i>
      
      





Yes, and it’s not very convenient to translate the names into codes each time, and then figure out what the icon is in the application code. It would be more convenient to use the friendly name of the icon - “arrow_back” like this:



 <Label Text="arrow_back">
      
      





For the solution, we will use the so-called behavior in the Russian translation of “Reactions to Xamarin.Forms Events” ( https://docs.microsoft.com/ru-ru/xamarin/xamarin-forms/app-fundamentals/behaviors/ )



To do this, create the GoogleMaterialFontBehavior class:



 public class GoogleMaterialFontBehavior: BehaviorBase<CustomLabel> { }
      
      





Our behavior will respond to changing label text, so Label will also have to be finalized:



 using System; using Xamarin.Forms; namespace MyApp.UserControls { public class CustomLabel : Label { public event EventHandler<EventArgs> TextChanged; public static readonly new BindableProperty TextProperty = BindableProperty.Create( propertyName: nameof(Text), returnType: typeof(string), declaringType: typeof(CustomLabel), defaultValue: "", defaultBindingMode: BindingMode.OneWay, propertyChanged: TextChangedHandler); public new string Text { get => (string)GetValue(TextProperty); set { base.Text = value; SetValue(TextProperty, value); } } private static void TextChangedHandler(BindableObject bindable, object oldValue, object newValue) { var control = bindable as CustomLabel; control.TextChanged?.Invoke(control, new EventArgs()); } } }
      
      





In the GoogleMaterialFontBehavior class, you need:





 namespace MyApp.Behaviors { public class GoogleMaterialFontBehavior: BehaviorBase<CustomLabel> { protected override void OnAttachedTo(CustomLabel bindable) { HandleTextChanged(bindable, null); bindable.TextChanged += HandleTextChanged; base.OnAttachedTo(bindable); } protected override void OnDetachingFrom(CustomLabel bindable) { bindable.TextChanged -= HandleTextChanged; base.OnDetachingFrom(bindable); } private void HandleTextChanged(object sender, EventArgs e) { var label = (CustomLabel)sender; if (label?.Text?.Length >= 2 && iconCodeDict.TryGetValue(label.Text, out var icon)) { label.FontFamily = fontFamily; label.Text = icon.ToString(); } } // private static readonly string fontFamily = Device.RuntimePlatform == Device.Android ? "MaterialIcons-Regular.ttf#MaterialIcons-Regular" : "MaterialIcons-Regular"; private static readonly Dictionary<string, char> iconCodeDict = new Dictionary<string, char> { {"3d_rotation", '\ue84d'}, {"ac_unit", '\ueb3b'}, {"access_alarm", '\ue190'}, … } } }
      
      





And now about the iconCodeDict reference itself. It will contain name-code pairs for font characters. In the case of google material icons, this data lies in a git repository in a separate file: github.com/google/material-design-icons/blob/master/iconfont/codepoints



Usage example



XAML:



at the top of the page add 2 namespaces:



 xmlns:uc="clr-namespace:MyApp.UserControls" xmlns:b="clr-namespace:MyApp.Behaviors"
      
      





and display the icon:



 <uc:CustomLabel Text="arrow_back" FontSize="30"> <Label.Behaviors> <b:GoogleMaterialFontBehavior /> </Label.Behaviors> </uc:CustomLabel>
      
      





C #:



 var label = new CustomLabel(); label.Behaviors.Add (new GoogleMaterialFontBehavior()); label.Text = "arrow_back";
      
      





Additionally



I also really liked the font Jam icons. Let's create a JamFontBehavior class for it, similar to GoogleMaterialFontBehavior. Others in it will be: fontFamily and iconCodeDict.



Download the font here: https://github.com/michaelampr/jam/blob/master/fonts/jam-icons.ttf

Icons to search here: https://jam-icons.com/



Very convenient search engine. Find the icon, click on it and the name on the clipboard. It remains to simply insert it into the code.



The algorithm is the same as for google icons, except for getting icon codes.



Codes must be taken from the svg file located in the same place .



Data can be easily pulled out using a text editor such as sublime. Select the line "data-tags", press Alt + F3, the multicursor is turned on. Next shift + home, ctrl + c, ctrl + a, ctrl + v. And so on. It is possible using Excel, to whom what is more familiar.



For example, the left arrow looks like this in the svg file:



 <glyph unicode="&#xe92b;" glyph-name="arrow-left" data-tags="arrow-left" d="M358.997 398.635l168.533-168.533c7.15-7.611 11.543-17.885 11.543-29.185 0-23.564-19.103-42.667-42.667-42.667-11.311 0-21.594 4.401-29.229 11.585l0.022-0.020-241.365 241.323c-7.749 7.706-12.545 18.376-12.545 30.165s4.796 22.459 12.544 30.163l241.367 241.367c7.769 8.036 18.647 13.026 30.69 13.026 23.564 0 42.667-19.103 42.667-42.667 0-12.043-4.989-22.92-13.014-30.678l-168.545-168.545h409.003c23.564 0 42.667-19.103 42.667-42.667s-19.103-42.667-42.667-42.667v0h-409.003z" />
      
      





After clearing the text, we get the contents of the glyph-name and unicode fields:



 {"arrow-left", '\ue92b'}
      
      





and add iconCodeDict to the dictionary



All Articles