Ensure frequent updates of Accessibility Elements

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.

Inspecting the Clock App with Xcode's Accessibility Inspector

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.

0:00
/0:17

VoiceOver experience of the StopwatchView without using the updates frequently trait

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)
0:00
/0:22

VoiceOver experience of the StopwatchView using the updates Frequently trait

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.