iOS Accessibility in SwiftUI: Create Accessible Charts using Audio Graphs

In this iOS accessibility tutorial, learn how to make charts in your app more accessible by using Audio Graphs. By David Piper.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Showing Many Data Series

Now that the precipitation chart is more accessible, it's time for the temperature chart.

By adding an Audio Graph for this chart, you'll learn two important differences to the first chart: Presenting a line chart and using many data series in one Audio Graph.

Open TemperatureChart.swift. A lot of what you've implemented in the previous sections already is created:

  • TemperatureChart implements AXChartDescriptorRepresentable.
  • There are two computed properties, temperatureChartTitle and temperatureChartSummary. They are similar to precipitationChartTitle and precipitationChartSummary you've added for PrecipitationChart.
  • The extension contains makeChartDescriptor, which is required by AXChartDescriptorRepresentable. It uses the title and summary and combines them with the axes and data series to create the Audio Graph.
  • makeXAxisDescriptor and makeYAxisDescriptor define the x- and y-axes. Again, this works similar to how you created axes for the previous chart. But instead of using a categorical and a numerical axes, this chart uses two numerical ones. This is because the x-axis shows the number of days instead of the names of months.

Make the chart visible as an audio graph by adding the following modifiers to the Canvas:

.accessibilityChartDescriptor(self)
.accessibilityElement(children: .combine)

The interesting part for you to implement is the data series descriptor, which you're going to add now. Replace the contents of makeDataSeriesDescriptor at the bottom of the extension with the following code:

// 1
var lowTemperatures: [AXDataPoint] = []
var highTemperatures: [AXDataPoint] = []

// 2
for measurement in measurements {
  let numberOfDay = Calendar.current.ordinality(
    of: .day,
    in: .year, 
    for: measurement.date
  ) ?? 30
  lowTemperatures.append(
    AXDataPoint(x: Double(numberOfDay), y: measurement.low.value)
  )
  highTemperatures.append(
    AXDataPoint(x: Double(numberOfDay), y: measurement.high.value)
  )
}

return [
  // 3
  AXDataSeriesDescriptor(
    name: "Low Temperatures",
    isContinuous: true,
    dataPoints: lowTemperatures
  ),

  // 4
  AXDataSeriesDescriptor(
    name: "High Temperatures",
    isContinuous: true,
    dataPoints: highTemperatures
  )
]
  1. To create an Audio Graph with more than one data series, you need to return an array of AXDataPoint for each data series. Here, you create two arrays, one for low temperatures per day and one for high ones. The user can then choose to either see and hear all data series at once or each one separately.
  2. Populate the lists by looping over measurements. For each item you create a new AXDataPoint for each data series. The x value represents the number of the day in the year. The y value is either the low or the high temperature, depending on the list you add the data point to.
  3. For PrecipitationChart you return one single array of data points. But, this time you return two. The first one is the data series for low temperatures. There is an important difference between this AXDataSeriesDescriptor and the one you implemented before. You set isContinuous to true instead of false. This results in a continuous line graph.
  4. Finally, you create the second AXDataSeriesDescriptor. This one represents the high temperatures.

Listening to many Data Series

Build and run the app. Select a weather station and open the temperature tab. If not active yet, turn on VoiceOver and navigate to the Audio Graph details page by focusing on the chart and swiping down. Once you hear Chart details, double-tap the screen.

This is what the Audio Graph looks like for two continuous data series:

A screenshot of the line chart for temperatures. It shows two lines, one for high and one for low temperatures. Below the chart is a play button and more information about the chart, e.g., a summary and features.

Swipe right until VoiceOver focuses Play and double-tap to play the Audio Graph. Again, a vertical line moves from left to right. Both the high and low temperature data sets play at the same time. This already provides some insights — you can hear the low and high temperatures follow a similar path.

But, it can get confusing to hear simultaneously more than one data series. Imagine having three, four or even five data series, perhaps with different paths. No one could understand it.

It's possible to select a specific data series. Above the chart is a button All Series. This means that the graph shows all data series at once and that VoiceOver plays all sounds together. You can change the selection to listen to specific data series.

Swipe left to select the button via VoiceOver. Double-tap the screen to open a context menu, where you can choose which data series to play:

A screenshot showing the same view as before. This time the context menu to select a series is open. It shows three options: All Series, Low Temperatures and High Temperatures.

Try it out and select Low Temperatures. Navigate to Play and listen again. This time it's only one tone and it's easier to follow.

Where to Go From Here?

You can download the completed version of the project using the Download Materials button at the top or bottom of this tutorial.

Accessibility is a critical, but often overlooked, element of apps. Audio Graph is an amazing feature that helps people who are blind or have reduced vision. They make charts more accessible, and it's super easy to use. All you need to do is help iOS create an audible representation of your charts data by defining axes and data sets.

If you want to learn more about accessibility and SwiftUI, check out iOS Accessibility in SwiftUI Tutorial Part 1, Part 2 and Part 3.

If you have any questions or comments, please join the forum discussion below!