Show errors
On Android, you can use a TextView to show an error message. The error message should also be posted to assistive technologies by using an accessibility announcement.
You can also use TextInputLayout, which makes showing error messages easier. Set setErrorEnabled) to true and then set the error message by using the setError method.
kotlin
textView.setVisibility(View.VISIBLE)
textView.text = "Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000"
input.setErrorEnabled(true)
input.setError("Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000") In Jetpack Compose, you can use error.error(kotlin.String)) function inside the semantics.semantics(kotlin.Boolean,kotlin.Function1)) block modifier to announce an error message to the accessibility service. Keep in mind that these announcements will not be shown visually. To show the message, use label property of TextField) in conjunction with isError, which automatically changes the appearance of the TextField).
kotlin
var errorMessage by remember { mutableStateOf("") }
val isError = remember(errorMessage) { errorMessage.isNotEmpty() }
TextField(
value = "",
label = {
Text(errorMessage.ifEmpty { "TextField label" })
},
isError = isError,
onValueChange = { /* State update logic */ },
modifier = Modifier
.semantics {
if (errorMessage.isNotEmpty()) {
error(errorMessage)
}
}
) On iOS, we recommend using an UILabel to indicate an error. The error message should also be posted to assistive technologies by using an accessibility announcement.
You could also use a third party library to display instructions. Unfortunately, accessibility is often not considered in the implementations.
swift
errorLabel.isHidden = false
errorLabel.text = "Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000" In SwiftUI, we recommend using Text or Label views to indicate an error. The error message should also be posted to assistive technologies by using an accessibility announcement.
swift
@State private var showError = false
@State private var errorMessage = ""
var body: some View {
VStack {
// Other UI elements here such as a TextField ...
Button(action: {
// Example action to trigger error
showError = true
errorMessage = "Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000"
}) {
Text("Schedule Appointment")
}
if showError {
Text(errorMessage)
.foregroundColor(.red)
.accessibilityLabel(errorMessage)
.onAppear {
// Post an accessibility announcement
UIAccessibility.post(notification: .announcement,
argument: errorMessage)
}
}
}
} With Flutter, you can set an InputDecoration on a TextField to indicate an error. Set the errorText property to the error message that should be displayed. To remove the error, set the errorText to null. The error message should also be posted to assistive technologies by using an accessibility announcement..
dart
bool _hasError = false;
TextField(
decoration: InputDecoration(
labelText: 'Date of birth',
helperText: _hasError ? 'Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000' : null,
),
); In React Native we recommend using a Text component to display an error. The error message should also be posted to assistive technologies by using an accessibility announcement.
You can also use a package for displaying errors, such as React Native Paper. This package includes a HelperText component which can be used for displaying errors. The type should be set to error for errors.
jsx
<Text>Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000</Text>
<View>
<TextInput label="Date of birth" value={text} onChangeText={onChangeText} />
<HelperText type="error" visible={hasErrors()}>
Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000
</HelperText>
</View> In MAUI, there is no built-in component for this, but you can hide or show a Label to display an error message.
Usage (C#)
csharp
label.Text = "This is an error message";
label.IsVisible = true; xml
<Label
Text="This is an error message"
IsVisible="True" /> In Xamarin.Forms, we recommend using a Label to display an error. The error message should also be posted to assistive technologies by using an accessibility announcement..
xml
<Label
Text="Invalid date, must be in the form DD/MM/YYYY, for example, 01/01/2000"
IsVisible="{Binding IsValid}" /> Use accessibility announcements
On Android, you can post an accessibility message by using the AccessibilityManager object. Create an AccessibilityEvent, set the type to AccessibilityEvent.TYPE_ANNOUNCEMENT and supply a message.
kotlin
val type = AccessibilityEventCompat.TYPE_ANNOUNCEMENT
val event = AccessibilityEvent.obtain(type)
event.text.add("Appt announcement")
event.className = Context::class.java.name
event.packageName = packageName
val accessibilityManager = ContextCompat.getSystemService(this, AccessibilityManager::class.java)
accessibilityManager?.sendAccessibilityEvent(event) In Jetpack Compose, to notify Composable state changes, you can use the liveRegion.liveRegion()) property from semantics.semantics(kotlin.Boolean,kotlin.Function1)) block modifier.
You can choose from two options for liveRegion:
LiveRegionMode.Polite) , which waits for the speech announcement in progress to complete
LiveRegionMode.Assertive), which interrupts ongoing speech to immediately announce changes
If you don't specify the liveRegion property, it indicates to Compose that updates to this field would not be announced.
kotlin
var changingText by remember{ mutableStateOf("Changing text") }
Text(
text = changingText,
modifier = Modifier.semantics {
liveRegion = LiveRegionMode.Polite
contentDescription = changingText // workaround for bug
}
) On iOS, you post an accessibility announcement by using the UIAccessibility object. The post method can be used to post data to assistive technologies. Set the type to announcement and supply a string argument to announce something.
You can also supply an NSAttributedString to customize the behavior of the announcement. For example, accessibilitySpeechQueueAnnouncement can be used to queue an announcement, instead of announcing it immediately. For more options, see: accessibility attribute keys of NSAttributedString.
swift
// Using String
UIAccessibility.post(
notification: .announcement,
argument: "Appt announcement"
)
// Using NSAttributedString
let message = NSAttributedString(
string: "Appt customized announcement",
attributes: [
.accessibilitySpeechQueueAnnouncement: true
]
)
UIAccessibility.post(
notification: .announcement,
argument: message
) In SwiftUI, you can enhance your app's accessibility by announcing interface changes to assistive technologies.
To achieve this, you can use the UIAccessibility object's post method. By setting the type to announcement and providing a String argument, you can deliver custom announcements to users who rely on assistive technologies like VoiceOver.
On iOS 17 and higher, you can also use AccessibilityNotification. In this case, create an AccessibilityNotification.Announcement with a String or AttributedString.
When providing an AttributedString, you can optionally customize the announcement behavior.
The announcement priority can be set with the accessibilitySpeechAnnouncementPriority property. Available options are: high, default and low.
For more options, check the AccessibilityAttributes struct.
swift
@State private var isLoading = false
var body: some View {
VStack {
Button("Search Appt website") {
isLoading = true
}
if isLoading {
ProgressView()
}
}
// Track state changes
.onChange(of: isLoading) { _, isLoading in
if isLoading {
announce("Search in progress")
}
}
}
// Post an announcement
func announce(_ message: String) {
if #available(iOS 17, *) {
// iOS 17+ can use AccessibilityNotification
var announcement = AttributedString(message)
announcement.accessibilitySpeechAnnouncementPriority = .high
AccessibilityNotification.Announcement(announcement).post()
} else {
// Lower iOS versions can use UIAccessibility
UIAccessibility.post(
notification: .announcement,
argument: message
)
}
} With Flutter, you can post an accessibility message by using the SemanticsService object. Use the announce method to post an accessibility announcement.
dart
SemanticsService.announce('Appt announcement', TextDirection.ltr); In React Native, you can post an accessibility message by using the AccessibilityInfo API. Use the announceForAccessibility method to post a message to assistive technologies.
jsx
AccessibilityInfo.announceForAccessibility('Appt announcement'); In MAUI, there is built-in support for posting accessibility announcements to the TalkBack or VoiceOver engine.
csharp
SemanticScreenReader.Default.Announce("Appt announcement"); csharp
public interface IA11YService
{
bool IsInVoiceOverMode { get; }
Task Speak(string? text, int pauseInMs = 0);
} csharp
private static AccessibilityManager? AccessibilityManager => Android.App.Application.Context.GetSystemService(Android.Content.Context.AccessibilityService) as AccessibilityManager;
public bool IsInVoiceOverMode => AccessibilityManager is { IsEnabled: true, IsTouchExplorationEnabled: true };
public async Task Speak(string? text, int pauseInMs = 0)
{
if (IsInVoiceOverMode && !string.IsNullOrEmpty(text))
{
if (pauseInMs > 0)
await Task.Delay(pauseInMs);
try
{
SemanticScreenReader.Announce(text);
}
catch (Exception e)
{
// Sometimes we get an exception: MauiContext must be set on parent
System.Diagnostics.Debug.WriteLine($"A11YService.Android.Speak exception: {e.Message}");
}
}
} csharp
public bool IsInVoiceOverMode => UIAccessibility.IsVoiceOverRunning;
public Task Speak(string? text, int pauseInMs = 0)
{
if (IsInVoiceOverMode && !string.IsNullOrEmpty(text))
{
if (pauseInMs > 0)
{
var dict = new NSMutableDictionary
{
{ UIView.SpeechAttributeQueueAnnouncement, new NSString("Yes") }
};
UIAccessibility.PostNotification(UIAccessibilityPostNotification.Announcement, new NSAttributedString(str: text, attributes: dict));
}
else
UIAccessibility.PostNotification(UIAccessibilityPostNotification.Announcement, new NSString(text));
}
return Task.CompletedTask;
} Xamarin Forms does not have built-in support for changing accessibility focus.
The SemanticExtensions file inside the Xamarin.CommunityToolkit contains the Announce method. It posts an accessibility announcement on the native platform.
csharp
SemanticExtensions.Announce("Appt announcement"); Use accessibility live region
On Android, a live region can be set by using the convience method setAccessibilityLiveRegion) of ViewCompat. To interrupt ingoing speech, also known as being assertive, use ACCESSIBILITY_LIVE_REGION_ASSERTIVE). To wait for ongoing speech, also known as being polite, use ACCESSIBILITY_LIVE_REGION_POLITE).
kotlin
// Interrupt ongoing speech
ViewCompat.setAccessibilityLiveRegion(view, ViewCompat.ACCESSIBILITY_LIVE_REGION_ASSERTIVE)
// Wait for ongoing speech
ViewCompat.setAccessibilityLiveRegion(view, ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE) In Jetpack Compose, a live region can be set inside the semantics.semantics(kotlin.Boolean,kotlin.Function1)) block modifier. To interrupt ongoing speech, also known as being assertive, use LiveRegionMode.Assertive). To wait for ongoing speech, also known as being polite, use LiveRegionMode.Polite).
kotlin
Text(
modifier = Modifier.semantics {
liveRegion = LiveRegionMode.Polite // or LiveRegionMode.Assertive
}
) On iOS, the closest thing to live regions are elements with the updatesFrequently trait. When an element is focused, label and value changes are announced periodically.
You can replicate a live region by posting accessibility announcements. To replicate 'polite' behavior, you can set accessibilitySpeechQueueAnnouncement to false. To be 'asssertive', set the value to true.
For even more advanced behavior, you can use act on announcementDidFinishNotification events.
swift
// Periodic announcements (only on focus!)
element.accessibilityTraits = .updatesFrequently
// Replicate live region
let message = NSAttributedString(
string: "Appt live region",
attributes: [.accessibilitySpeechQueueAnnouncement: true]
)
UIAccessibility.post(notification: .announcement, argument: message) In SwiftUI, the closest thing to live regions are elements with the updatesFrequently trait. When an element is focused, label and value changes are announced periodically.
You can also replicate a live region by posting accessibility announcements. To replicate 'polite' behavior, you can set accessibilitySpeechQueueAnnouncement to false. To be 'assertive', set the value to true.
For even more advanced behavior, you can act on announcementDidFinishNotification events.
WARNING: You likely should not mix updatesFrequently trait and manual announcements. Choose one approach.
swift
@State var stockPrice = "Stock Price: $123.45"
var body: some View {
// Periodic announcements (only on focus!)
Text(stockPrice)
// Add the updatesFrequently trait here
.accessibilityAddTraits(.updatesFrequently)
.onChange(of: stockPrice) { _, newPrice in
// Replicate live region
let message = NSAttributedString(
string: newPrice,
attributes: [.accessibilitySpeechQueueAnnouncement: true]
)
UIAccessibility.post(notification: .announcement, argument: message)
}
} On Flutter, the liveRegion property can be used in Semantics to indicate a live region. By default, the live region is polite: it queues announcements.
dart
Semantics(
liveRegion: true,
child: Text('Appt live region')
); On React Native, the accessibilityLiveRegion prop can be used to indicate a live region. The value can be set to asssertive to interrupt ongoing speech to for immediate announcements on change. The polite value can be used to queue announcements. The none value can be used to disable announcements on change.
jsx
<Text accessibilityLiveRegion="assertive|polite|none">
Appt live region
</Text> IN MAUI, there is no built-in support to indicate an accessibility live region.
By using Handlers, it is possible to implement platform-specific behavior. Alternatively, you can use the accessibility Announce helper method to announce any message when you update the UI data.
csharp
SemanticScreenReader.Default.Announce("This is the announcement text."); Xamarin Forms does not have built-in support to indicate an accessibility live region. By using Effects it is possible to implement platform specific behaviour. The A11YEffect, A11YEffect for Android and A11YEffect for iOS files show how to implement an effect to replicate an accessibility live region.
xml
<controls:CustomFontLabel
effects:A11YEffect.ControlType="{OnPlatform iOS=LiveUpdate, Android=LiveUpdate}" />