Skip to content

Accessibility Actions (SwiftUI)

A SwiftUI guide for coding agents. Also covers voiceover custom actions, adjustable rating control, accessibilityadjustableaction, magic tap, escape gesture dismiss, named accessibility action.

SwiftUI views are accessible by default, but a composite control like a five-star rating reads as five meaningless "star fill" buttons. Custom accessibility actions turn it into one adjustable element, and predefined gestures like magic tap map to the view's main action.

Adjustable Elements

Collapse the container into one element with accessibilityElement(), give it a label and a value, and handle accessibilityAdjustableAction, which also adds the adjustable trait. VoiceOver then announces "rating, 3, adjustable" and swipe up or down changes the value.

struct RatingView: View {
  @Binding var rating: Int

  var body: some View {
    HStack {
      ForEach(1..<6) { index in
        Button(action: { rating = index }) {
          Image(systemName: index <= rating ? "star.fill" : "star")
        }
      }
    }
    .accessibilityElement()
    .accessibilityLabel(Text("rating"))
    .accessibilityValue(Text(String(rating)))
    .accessibilityAdjustableAction { direction in
      switch direction {
      case .increment:
        guard rating < 5 else { break }
        rating += 1
      case .decrement:
        guard rating > 1 else { break }
        rating -= 1
      @unknown default:
        break
      }
    }
  }
}

A stack is normally a transparent accessibility container that exposes its children; accessibilityElement() replaces that with one element and hides the children.

Predefined and Named Actions

Magic tap (two-finger double tap) should trigger the view's main action; escape (two-finger z-scrub) should back out or dismiss.

PlayerContent()
  .accessibilityAction(.magicTap) {
    viewModel.isPlaying ? viewModel.pause() : viewModel.play()
  }
  .accessibilityAction(.escape) {
    viewModel.pause()
    presentation.wrappedValue.dismiss()
  }

Named actions appear in the VoiceOver rotor menu for the element, giving swipe-reachable alternatives to small buttons.

.accessibilityAction(named: Text("skip")) { viewModel.skip() }
.accessibilityAction(named: Text("repeat")) { viewModel.repeat() }

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-accessibility-actions" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems