Ensure frequent updates of Accessibility Elements
Learn how to ensure frequent updates of Accessibility Elements using the Updates Frequently trait.
To provide a good accessibility experience for VoiceOver users, it's crucial to keep the accessible user interface information up-to-date. Normally, when an element is focused, VoiceOver reads its accessibility properties only once.
However, for elements that exhibit real-time dynamic information or content that changes rapidly, such as a stopwatch showing elapsed hours, minutes, and seconds, the information will become outdated quickly. In such cases, we must ensure that the information is not only accessible but also current. VoiceOver should announce accessibility properties like the label or the value periodically whenever the element has focus or based on the changes it receives.
By using the trait UIAccessibilityTraitUpdatesFrequently
, we can instruct the accessible user interface to regularly poll an element for changes, ensuring that VoiceOver delivers timely and accurate updates.
How to use it
Both UIKit and SwiftUI have a predefined trait for frequently updated elements that can be applied to every element as discussed in Preparing your App for VoiceOver: Accessibility Traits.
When to use it
Depending on the UI framework used, there are several factors to consider
SwiftUI
Due to the declarative nature of SwiftUI, the accessible user interface (AUI) rebuilds itself whenever the associated view refreshes. This means that SwiftUI automatically manages updates to the AUI based on changes to the underlying data. Consequently, in many cases, explicitly implementing the .updatesFrequently
trait on a View may not be necessary.
However, consider the following example:
struct StopwatchView: View {
@State private var startTime = Date()
private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State private var elapsedTime: TimeInterval = 0
private var timecode: String {
let hours = Int(elapsedTime) / 3600
let minutes = Int(elapsedTime) / 60 % 60
let seconds = Int(elapsedTime) % 60
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}
var body: some View {
Text(timecode)
.onReceive(timer) { _ in
elapsedTime = Date().timeIntervalSince(startTime)
}
.onTapGesture {
startTime = Date()
elapsedTime = 0
}
.padding()
.font(Font.system(.title, design: .monospaced))
}
}
Here, a timer is displayed using a computed property. VoiceOver will not provide audible feedback regarding changes to the timer, even as it continues to count down in real time.
This is the VoiceOver experience without using the .updatesFrequently
modifier.
By incorporating the .updatesFrequently
trait, VoiceOver will regularly announce updates based on changed values in the Text View.
Text(timecode)
.onReceive(timer) { _ in
elapsedTime = Date().timeIntervalSince(startTime)
}
.onTapGesture {
startTime = Date()
elapsedTime = 0
}
.padding()
.font(Font.system(.title, design: .monospaced))
.accessibilityAddTraits(.updatesFrequently)
UIKit
In UIKit views don't automatically reload in real time based on changes. This means that when values in the UI are updated programmatically, UIKit doesn't trigger VoiceOver announcements to reflect these changes.
In addition to utilizing the .updatesFrequently
trait, developers can programmatically determine a VoiceOver announcement using the UIAccessibility.post(notification: , argument: )
method. This involves passing a notification object of type UIAccessibility.Notification
and the corresponding argument to prompt VoiceOver to announce the desired information.
The .updatesFrequently
trait should be used when an accessibility element that updates its label or value too frequently to send update notifications in order to avoid continual notifications and, instead, poll for changes when it needs updated information.