An application which explores autosizing a label in Xamarin.Forms.

Overview

The ability for the system to autosize a label depending on text length is pretty fundamental, especially when considering localization. Coming from iOS Development and Unity, this is something I expected to be build into Xamarin.Forms, and was a little perplexed that I needed to invest so much time in reading docs, forms and inventually needing to roll my own solution.

Case #1

Consider that a label spans a single line. As the German string may be longer than the English string, the fontsize should automatically resize (scale-down) to fit all text on a single line.

Case #2

Consider that a label should maximize the font size (scale-up) so that the text takes up the full rectangular space.

Solution

For #1, we could use XamarinCommunityToolkit NuGet package with the LabelSizeFontToFit effect:

<Label.Effects>
  <effects:LabelSizeFontToFit />
</Label.Effects>

This method, however, will not scale up a font size, only down. Thus, to solve #2, I decided to adopt Charles Petzold’s generally approach of Empirically fitting text in Chapter 5 Dealing with Sizes of his book Creating Mobile Apps with Xamarin.Forms into my own custom label.

DALabel extends Xamarin.Forms.Label and adds the following two methods:

/// <summary>
/// Autosizes the label's font size with regards to it's container size.
/// </summary>
private void AutoFontSize()
{
    //determine the text height for the min font size
    double lowerFontSize = 10;
    double lowerTextHeight = TextHeightForFontSize(lowerFontSize);

    //determine the text height for the max font size
    double upperFontSize = 100;
    double upperTextHeight = TextHeightForFontSize(upperFontSize);

    //start a loop which'll find the optimal font size
    while(upperFontSize - lowerFontSize > 1)
    {
        //determine current average font size and calculate corresponding text height
        double fontSize = (lowerFontSize + upperFontSize) / 2;
        double textHeight = TextHeightForFontSize(upperFontSize);

        //if the calculated height is out of bounds, update max values, else update min values
        if(textHeight > Height)
        {
            upperFontSize = fontSize; upperTextHeight = textHeight;
        }
        else
        {
            lowerFontSize = fontSize; lowerTextHeight = textHeight;
        }
    }

    //finally set the correct font size
    FontSize = lowerFontSize;
}

/// <summary>
/// Determines the text height for the label with a given font size.
/// </summary>
private double TextHeightForFontSize(double fontSize)
{
    FontSize = fontSize;
    return OnMeasure(Width, Double.PositiveInfinity).Request.Height;
}

Now, whenever the label’s size is updated, the fontsize will be adjusted.

/// <summary>
/// Callback when the size of the element is set during a layout cycle.
/// </summary>
protected override void OnSizeAllocated(double width, double height)
{
    //call base implementation
    base.OnSizeAllocated(width, height);

    //update font size
    AutoFontSize();
}

Likewise, whenever the text itself is actually set, we can update the font size

/// <summary>
/// The label's text.
/// </summary>
new public string Text
{
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); AutoFontSize(); }
}

As an added bonus, MinFontSize and MaxFontSize properties can be added so that the min/max font size values can be specified in XAML.

Resources

Charles Petzold Creating Mobile Apps with Xamarin.Forms

Charles Petzold Chapter 5. Dealing with Sizes

Xamarin Community Toolkit

Xamarin.Forms.Label Source

This post was generated from a GitHub repository.