VoiceOver For Frequently Updating Data

A quick tip and reminder if you’re showing something that updates frequently you need to let VoiceOver know.

What’s The Problem?

Here’s my situation. I have a running timer showing a video timecode (hour, minute, second, frame). This is the user interface captured several seconds apart:

Three timecodes a few seconds apart

For the curious, here’s the SwiftUI view:

struct ContentView: View {
  @State private var now = Date()
  private let timer = Timer.publish(every: 0.04, 
    on: .main, 
    in: .common)
  private var timecode: Timecode {
  var body: some View {
      .onReceive(timer) { input in
        now = input
      .font(Font.system(.title, design: .monospaced))
      .overlay(RoundedRectangle(cornerRadius: 8)

The Timecode type is a small struct that breaks the time into the required components and has a convenient time formatter. It works fine but it does have an accessibility problem.

VoiceOver does a good job of reading the timecode value. It knows that it’s some sort of time. In my locale it reads the timecode as follows:

“Twelve hours, three minutes and twenty seven seconds zero seven”

I could improve that as it does not announce that the “zero seven” are frames. I might even decide that the frame value changes so fast it’s purely decorative and override the accessibility label to not include it while running.

The bigger problem is that VoiceOver only reads the value once. That’s not much use to somebody with low vision.

Updates Frequently Trait

I think one of the reasons I like making iOS apps accessibile is that the Apple engineers have done most of the hard work for us. The solution to my problem is one line of code.

If you’re using SwiftUI add the updatesFrequently accessibility trait to the text view:

// SwiftUI

If you’re using UIKit you can set the trait directly in Interface Builder:

Xcode accessibility settings for label with updates frequently ticked and highlighted in a red box

Or if building your layout in code:

// UIUKit

VoiceOver now periodically re-reads the timecode label whenever the label has the focus.