Skip to content

The accessibilityChildren View Modifier (SwiftUI)

A SwiftUI guide for coding agents. Also covers accessible chart shape, voiceover for custom drawing, accessibility container swiftui, accessibilitychildren vs accessibilityrepresentation, accessibility tree for path.

A custom Shape collapses into one view once you fill or stroke it, so VoiceOver cannot reach the individual data points it draws. accessibilityChildren fixes that: it builds an accessibility container from a phantom view you describe, without rendering it.

The Problem: A Shape Is One Element

A bar chart drawn as a single Path gives VoiceOver one opaque element, whatever the number of bars.

struct BarChartShape: Shape {
  let dataPoints: [DataPoint]

  func path(in rect: CGRect) -> Path {
    Path { p in
      let width = rect.size.width / CGFloat(dataPoints.count)
      var x: CGFloat = 0
      for point in dataPoints {
        let pointRect = CGRect(
          x: x,
          y: rect.size.height - point.value,
          width: width,
          height: rect.size.height
        )
        p.addPath(RoundedRectangle(cornerRadius: 8).path(in: pointRect))
        x += width
      }
    }
  }
}

Describe the Children With a Phantom View

Attach accessibilityChildren with a ViewBuilder closure. SwiftUI never renders that closure: it only reads it to populate the accessibility tree, one element per bar.

BarChartShape(dataPoints: dataPoints)
  .fill(.red)
  .accessibilityLabel("Chart")
  .accessibilityChildren {
    HStack(alignment: .bottom, spacing: 0) {
      ForEach(dataPoints) { point in
        RoundedRectangle(cornerRadius: 8)
          .accessibilityValue(Text(point.value.formatted()))
      }
    }
  }

VoiceOver now announces the chart as a container and steps through each bar's value.

Children Versus Representation

accessibilityChildren only adds a container of child elements and leaves the view's own accessibility untouched. accessibilityRepresentation replaces the view's whole accessibility tree. Use children to enrich a drawing, use representation to stand in for it.

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-the-power-of-accessibilitychildren-view-modifier" })
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