Using a custom font in Xamarin Forms Entry Placeholder

After stumbling upon this article the other day, which revisits the topic of custom fonts, it reminded me of an issue I encountered a while ago; giving your Entry a different font for both the placeholder and the text.

By default, Xamarin Forms will assign an Entry's placeholder the same font as its text, using custom renderers, we can override this to add the desired functionality.

Adding custom fonts to your library

In order to use custom fonts within your Xamarin apps you first need to register them. Follow Gerald Versluis' blog post on how to import and register your custom font files on each platform.

Add extended control to Shared library

First, create a custom control in your shared code which extends from Entry and add an additional BindableProperty to store your placeholder font.

public class CustomPlaceholderEntry : Entry
{
    public static readonly BindableProperty PlaceholderFontFamilyProperty
            = BindableProperty.Create(nameof(PlaceholderFontFamily), 
                                      typeof(string), 
                                      typeof(CustomPlaceholderEntry), 
                                      default(string));

    public string PlaceholderFontFamily
    {
        get { return (string)GetValue(PlaceholderFontFamilyProperty); }
        set { SetValue(PlaceholderFontFamilyProperty, value); }
    }
}

You can extend this further if you want to include properties such as the placeholder's colour and size.

iOS

  1. Create a custom renderer which inherits from EntryRenderer.
  2. Add the following code to load your font into a new variable and assign it to an NSAttributedString which can be passed to the native control:
private void UpdatePlaceholderFont()
{
    ...
		var descriptor = new UIFontDescriptor().CreateWithFamily(CustomElement.PlaceholderFontFamily);
    var placeholderFont = UIFont.FromDescriptor(descriptor, (float)CustomElement.FontSize);
    ...
    Control.AttributedPlaceholder = new NSAttributedString(CustomElement.Placeholder, placeholderFont);
}
  1. Call UpdatePlaceholderFont() from the OnElementChanged & OnElementPropertyChanged
  2. Register your custom renderer and providing you've registered your fonts correctly and declared them in the Info.plist, you should be able to see a custom font set for the placeholder of your entry.

Android

Implementing a custom placeholder font on Android is a little more complex than iOS, but still achievable. As with iOS, the native Android control also exposes a property that allows for a custom placeholder. in this case, it's Control.HintFormatted.

The Control.HintFormatted property can be assigned a TypefaceSpan, however, out of the box, this doesn't expose all the functionality we need. Therefore, first we have to create our own CustomTypefaceSpan which will allow us to set some additional properties.

public class CustomTypefaceSpan : TypefaceSpan
{
    private readonly Typeface _customTypeface;

    public CustomTypefaceSpan(Typeface type) : base("")
    {
        _customTypeface = type;
    }

    public CustomTypefaceSpan(string family, Typeface type) : base(family)
    {
       customTypeface = type;
    }

    public override void UpdateDrawState(TextPaint ds)
    {
        ApplyCustomTypeFace(ds, _customTypeface);
    }

    public override void UpdateMeasureState(TextPaint paint)
    {
        ApplyCustomTypeFace(paint, _customTypeface);
    }

    private static void ApplyCustomTypeFace(Paint paint, Typeface tf)
    {
        paint.SetTypeface(tf);
    }
}

Now we have our CustomTypefaceSpan we can create a new custom renderer.

  1. As with iOS, create a new custom renderer which extends from EntryRenderer.
  2. Create an UpdatePlaceholderFont method as outlined below:
private void UpdatePlaceholderFont()
{
    ...

    var placeholderFontSize = (int)CustomElement.FontSize;
    var placeholderSpan = new SpannableString(CustomElement.Placeholder);
						
    // Set Fontsize
    placeholderSpan.SetSpan(new AbsoluteSizeSpan(placeholderFontSize, true), 0, placeholderSpan.Length(), SpanTypes.InclusiveExclusive); 

    var typeFace = FindFont(CustomElement.PlaceholderFontFamily);
    var typeFaceSpan = new CustomTypefaceSpan(typeFace);
		
    //Set Fontface
    placeholderSpan.SetSpan(typeFaceSpan, 0, placeholderSpan.Length(), SpanTypes.InclusiveExclusive); 

    Control.HintFormatted = placeholderSpan;
}
  1. Call UpdatePlaceholderFont() from the OnElementChanged & OnElementPropertyChanged

There we have it, a very brief guide on how to change your Entry's placeholder font. If the code here is too sparse, you can find a full working demo on my GitHub (apologies in advance for the use of Comic Sans 😅).

Full demo source: github.com/adenearnshaw/XamCustomPlaceholderFont