How Do I Support Accessibility Font Sizes in Xamarin Forms?

Using the info in the answer provided @SushiHangover, I was able to implement the following renderer strategy for iOS:

Create a renderer for each control that you want to support dynamic text on. In my case, this included Labels, Buttons, and Entrys. The renderers assign the Control.Font to a UIFontDescriptor with the PointSize percentage adjusted based on the NamedSize assigned to the Xamarin Forms control.

Example:

[assembly: ExportRenderer(typeof(Button), typeof(CustomButtonRenderer))]
namespace iOS.Controls
{
    public class CustomButtonRenderer : ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            var view = e.NewElement as Button;
            if(Control != null && view != null)
            {
                var descriptor = UIFontDescriptor.PreferredBody;
                var pointSize = descriptor.PointSize;

                var size = view.FontSize;
                if(size == Device.GetNamedSize(NamedSize.Large, typeof(Button)))
                {
                    pointSize *= 1.4f;
                }
                else if(size == Device.GetNamedSize(NamedSize.Small, typeof(Button)))
                {
                    pointSize *= .8f;
                }
                else if(size == Device.GetNamedSize(NamedSize.Micro, typeof(Button)))
                {
                    pointSize *= .6f;
                }

                Control.Font = UIFont.FromDescriptor(descriptor, pointSize);
            }
        }
    }
}

You could even dynamically support fixed font sizes this way by taking their percentage value based on the base NamedSize.Default size.

Example:

[assembly: ExportRenderer(typeof(Button), typeof(CustomButtonRenderer))]
namespace iOS.Controls
{
    public class CustomButtonRenderer : ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
        {
            base.OnElementChanged(e);

            var view = e.NewElement as Button;
            if(Control != null && view != null)
            {
                var descriptor = UIFontDescriptor.PreferredBody;
                var percent = view.FontSize / Device.GetNamedSize(NamedSize.Default, typeof(Button));

                Control.Font = UIFont.FromDescriptor(descriptor, percent * descriptor.PointSize);
            }
        }
    }
}

For reference, full implementation examples can be found in this GitHub project.


You would need to supply the UIFont returned from preferredFontWithTextStyle (C# = UIFont.PreferredFontForTextStyle) as your usage context of a label, button, entry, etc... would not be known to Xamarin.Forms.

So what I did for one client was subclass the base renderers and view elements and add an iOS-only property to those elements so they could define the context of how that control is begin used in the UI and thus when rendered by iOS these controls will be subject to Dynamic Text sizing.

There are six Dynamic font types defined in iOS 9:

  • UICTFontTextStyleBody
  • UICTFontTextStyleCaption1
  • UICTFontTextStyleCaption2
  • UICTFontTextStyleFootnote
  • UICTFontTextStyleHeadline
  • UICTFontTextStyleSubhead

Note: Xamarin.iOS does not have constants/enum defined for these like Swift does (ObjC does not define these either), so they are passed as a NSString, see example below.

Example Renderer:

Sets UICTFontTextStyleBody for a label subclass called BodyLabel:

[assembly: ExportRenderer(typeof(BodyLabel), typeof(iOSLabelBodyRenderer))]
namespace Foobar.iOS
{
    public class iOSLabelBodyRenderer : LabelRenderer
    {
        public iOSLabelBodyRenderer() { }

        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);
            if (Control != null)
                Control.Font = UIFont.GetPreferredFontForTextStyle(new NSString("UICTFontTextStyleBody"));
        }
    }
}

Results in:

enter image description here

enter image description here

Note: Technically you should also implement UIContentSizeCategoryDidChangeNotification notifications so you resize/invalidate your controls when the user changes the dynamic font size.


For iOS I believe things have changed in Xamarin.Forms since the earlier answers were provided. If a NamedSize value is assigned to the FontSize property of a text element, the text now gets scaled up and down with the value set via the iOS "Larger Text" setting.

For example, this would result in accessible, scalable text:

<Label
    FontSize="Large"
    LineBreakMode="WordWrap"
    Text="Some text here." />