[Bug] TableView's SwitchCell does not function correctly with screenreaders #10852
Description
Description
Using a native screenreader (VoiceOver on iOS; TalkBack on Android), activating a SwitchCell in a settings TableView reads out the current state of the switch (On or Off), but does not toggle it.
On Android, selecting the switch within the cell and activating it does toggle it, but activating the row does not.
On iOS, the SwitchCell cannot be toggled.
Steps to Reproduce
- Create a template Xamarin.Forms project
- Add a TableView with SwitchCells to a page as per the official example
- Build and deploy to a physical device (iOS or Android)
- Enable the device's native screenreader
- Navigate to the page, highlight one of the SwitchCell rows
- Double Tap to activate
Expected Behavior
The switch should toggle and the screenreader should announce the new switch state.
Actual Behavior
The switch does not toggle and the screenreader announces the unchanged switch state.
SwitchCell's OnChanged event does not fire (Checked with breakpoints).
- Or -
On Android, the switch itself can be highlighted (with difficulty - the interaction area is small) and activating it does toggle the state. However, the switch does not utilise any of the SwitchCell's AutomationProperties, so it is unlabelled and unannounced - the screenreader simply annouces "On switch" or "Off switch".
Basic Information
- Version with issue: Xamarin.Forms 4.5.0.495
- Last known good version: N/A
- IDE: Visual Studio Community 2019
- Platform Target Frameworks:
- iOS: 8.0.30703
- Android: 9.0
- UWP: Not tested
- Nuget Packages:
- Newtonsoft
- SkiaSharp
- Xamarin.Auth
- Xamarin.Essentials
Workaround
Create custom SwitchCell element, handling events and bindings manually within.
Sample code for workaround:
CustomSwitchCell.xaml
<ViewCell
x:Class="AmanoPal.Views.CustomControls.CustomSwitchCell"
AutomationProperties.IsInAccessibleTree="False"
Tapped="ViewCell_Tapped"
mc:Ignorable="d">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="0"
Margin="18,0"
InputTransparent="True"
Text="{Binding Text, Source={RelativeSource AncestorType={x:Type local:CustomSwitchCell}, Mode=FindAncestor}}"
VerticalOptions="Center" />
<Switch x:Name="ToggleSwitch"
Grid.Column="1"
Margin="4,0"
InputTransparent="True"
IsToggled="{Binding IsToggled, Source={RelativeSource AncestorType={x:Type local:CustomSwitchCell}, Mode=FindAncestor}, Mode=OneWay}"
VerticalOptions="Center" />
</Grid>
</ViewCell>
CustomSwitchCell.xaml.cs
public partial class CustomSwitchCell : ViewCell
{
public CustomSwitchCell()
{
InitializeComponent();
}
private void ViewCell_Tapped(object sender, EventArgs e) => IsToggled = !IsToggled;
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public bool IsToggled
{
get => (bool)GetValue(IsToggledProperty);
set => SetValue(IsToggledProperty, value);
}
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(string), typeof(CustomSwitchCell), null);
public static readonly BindableProperty IsToggledProperty =
BindableProperty.Create("IsToggled", typeof(bool), typeof(CustomSwitchCell), null, BindingMode.TwoWay);
}