Xamarin Forms offers these attributes helping you to make apps accessible:

  • AutomationProperties.IsInAccessibleTree - indicates whether the element is available to an accessible application.
  • AutomationProperties.Name - a short description of the element that serves as a speakable identifier for the element.
  • AutomationProperties.HelpText - a longer description of the element, which can be thought of as tooltip text associated with the element.
  • AutomationProperties.LabeledBy - allows another element to define accessibility information for the current element.

However, when you test your app on both iOS and Android you’ll see different behavior in how the voice over feature of these platforms handles these attributes for labels.

Given this label:

<Label AutomationProperties.IsInAccessibleTree="True"
       Text="The text"
       AutomationProperties.Name="The name"
       AutomationProperties.HelpText="The help text"/>

iOS will read: “The name… The help text.”

Android will read: “The text, the name… The help text.”

The difference between the two platforms is that iOS will not speak the text of the view when a name has been set. This is important to know when you want voice over both platforms to behave similar. We can cope with this by not setting a name on a view that has text on Android only:

<Label AutomationProperties.IsInAccessibleTree="True"
       Text="The text"
       AutomationProperties.Name="{OnPlatform iOS='The accessible text', Android=''}"
       AutomationProperties.HelpText="The help text"/>

This is the most simple solution but removes the functionality of providing a little better short description for Android users.

To achieve the same behavior on Android as well as iOS a custom label renderer should be created:

using System.ComponentModel;
using Android.Content;
using Xamarin.Forms;

[assembly: ExportRenderer(typeof(Label), typeof(A11y.Droid.Renderers.LabelRenderer))]
namespace A11y.Droid.Renderers
{
    public class LabelRenderer : Xamarin.Forms.Platform.Android.LabelRenderer
    {
        public LabelRenderer(Context context)
            : base(context)
        {
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            var name = AutomationProperties.GetName(Element);
            var text = Element.Text;
            var helpText = AutomationProperties.GetHelpText(Element);

            if (string.IsNullOrEmpty(name))
            {
                Control.ContentDescription = $"{text}";
            }
            else
            {
                Control.ContentDescription = $"{name}";
            }

            Control.Hint = helpText;
        }
    }
}

Note: This blog post was created using Xamarin.Forms 5.0.0.1874