I want to let the user choose the font in my xamarin forms application. I have around 16 fonts which size around 10 MB already my app size is too big this will cause more size. Is there anyway I can download fonts when user want to change the font ? please help.
I've created a sample which does exactly what you need. You can find the source code HERE. Let me explain what I've done.
From architectural perspective we need to do the following
Download font files from backend
Cache those files on device
Make sure they work on iOS and Android. In this step we need to do some device specific tweaking to make the fonts work. For iOS we need to register the fonts with the iOS runtime. For Android we need to create custom renderers for controls for which we want to use custom fonts. Read further for details.
A demo ViewModel and View to demonstrate the functionality.
From code perspective I've done the following to implement above mentioned architecture
Create service for loading fonts
IFontService
- service to download and install fonts on local devices
public interface IFontService
{
//Directory where we'll keep custom fonts
IFolder FontFolder { get; }
//Contains mapping between font name and font file
Dictionary<string, string> Fonts { get; }
//Will download and install custom fonts and instantiate the Fonts mapping property
Task LoadAvailableFonts();
}
Implement the IFontService
BaseFontService
- implements most part of the IFontService
which is not device specific. It has two abstract methods which should be implemented device specific for caching and installing fonts.
//We will cache all the fonts in caches folder, which is device specific,
//That's why we use abstract property which we'll implement separately for iOS and android
//and inject into PCL using dependency injection
protected abstract Task<IFolder> GetCacheFolder();
//For iOS we need to register custom fonts with iOS runtime.
//Check FontService implementation for more details.
protected abstract Task InstallFonts(IEnumerable<CustomFont> fonts);
Implement device specific stuff and use dependency injection
In iOS and Android projects I've created FontService
implementation which derives from BaseFontService
and implements device specific logic - font installation and caching.
Android
For Android we only need to override GetCacheFolder()
, we don't need extra installation step for Android, that's why we can leave InstallFonts()
implementation empty.
Another tweak that we need for Android is custom renderers for those controls for which we want to use custom font. In iOS this step is not needed, because when you register fonts you can use them all around the application. In my sample I've created ButtonRenderer
and LabelRenderer
. They simply listen to changes of FontFamily
property and then try to find that font using IFontService
. I've created a FontToolbox
class which which implements font loading logic in TryGetFont
method. If you want to support custom fonts for other controls, you can use similar pattern as in Button/Label renderers and use TryGetFont
to load actual font.
iOS
For iOS it's bit more complicated. We need to load the font files from the cache directory and register them using ios font management API. Refer here for more details
Demo code
I've created FontDemoViewModel which will call IFontService
, load available fonts into a collection to which we'll bind in our view.
I've created FontDemoPage which will allow select a font from a picker (for that I've added BindablePickerEx
control) and update fonts of a button and a label.
Some notes
Make sure the font file name will match PostScript name of the font. This is not required for Android, because Android will be able to load a font with arbitrary name, because it just needs a reference to a font file. However for iOS you need to make sure this is true, as the run-time searches for fonts by their PostScript name. Here you can find how to get the PostScript name of a font.
Another thing is that in this sample for simplicity sake I've embedded all the fonts into assembly resources. In my BaseFontService
I load all the fonts from assembly resources in this method
private async Task<List<CustomFont>> DownloadFonts()
For your implementation you'll need to make actual web requests here to download fonts from your backend.