Making custom SwiftUI controls accessible with accessibilityRepresentation
Learn how to give a custom control the accessibility behavior of a native SwiftUI control.
19 Oct 2026 · 5 min read
Native SwiftUI controls already provide information and interactions that assistive technologies can understand. When we build a control entirely from custom views and gestures, we also become responsible for providing that accessibility behavior.
In this article, we'll build a waveform scrubber and use accessibilityRepresentation to give it the accessibility behavior of a native Slider.

Building a custom waveform scrubber
Suppose an audio player uses a row of bars to visualize its playback progress. Dragging across the waveform changes the current position:
struct WaveformScrubber: View {@Binding var position: TimeIntervallet duration: TimeIntervalvar body: some View {GeometryReader { geometry inWaveformBars(progress: duration > 0 ? position / duration : 0).contentShape(.rect).gesture(DragGesture(minimumDistance: 0).onChanged { value inguard duration > 0, geometry.size.width > 0 else {return}let progress = min(max(value.location.x / geometry.size.width, 0),1)position = duration * progress})}.frame(height: 44)}}
In the full implementation, the individual waveform bars are drawn and sized using GeometryReader. Here, WaveformBars keeps the rendering details out of the example so we can focus on the binding and drag interaction.
The control works with touch, but its appearance and DragGesture don't tell assistive technologies that it represents an adjustable playback position. Adding only an accessibility label would describe the control without providing a way to change its value.
Reusing a Slider's accessibility behavior
The waveform behaves differently from a native Slider visually, but both controls select a value from a bounded range. We can express that relationship by adding accessibilityRepresentation to the complete control:
.accessibilityRepresentation {Slider(value: $position, in: 0...duration, step: 1) {Text("Playback position")}.accessibilityValue(formattedPosition)}
The formatted value communicates a playback time instead of a raw number of seconds:
private var formattedPosition: String {Duration.seconds(position).formatted(.time(pattern: .minuteSecond))}
SwiftUI doesn't display the Slider from the representation. It uses the slider to create the waveform's accessibility label, value, traits and adjustable interaction. Because both controls use the same position binding, changing the value through an assistive technology also updates the visible waveform.
This avoids recreating the semantics of an adjustable control manually and keeps the accessibility behavior aligned with the native control.
Conclusion
accessibilityRepresentation is useful when the visible control needs a genuinely custom implementation but still corresponds to a familiar native control. The waveform is custom-drawn and responds to a drag gesture, while its value and interaction model correspond to a Slider.



Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips



