一、DataPoint 结构体
在 SwiftUI 中构建一个简单的条形图视图开始,该视图使用垂直条形显示一组数据点。如下所示,有一个 DataPoint 结构,用于描述条形图视图中的条形,它具有 id、标签、数值和填充颜色:
struct DataPoint : Identifiable {
let id = UUID ( )
let label: String
let value: Double
let color: Color
}
二、BarChartView 结构体
接下来,可以定义一个条形图视图,它接受一组 DataPoint 结构体实例并将它们显示出来,如下所示,有一个 BarChartView,它接收一组 DataPoint 实例并将它们显示为水平堆栈中不同高度的圆角矩形:
struct BarChartView : View {
let dataPoints: [ DataPoint ]
var body: some View {
HStack ( alignment: . bottom) {
ForEach ( dataPoints) { point in
VStack {
RoundedRectangle ( cornerRadius: 8 , style: . continuous)
. fill ( point. color)
. frame ( height: point. value * 50 )
Text ( point. label)
}
}
}
}
}
三、ContentView 结构体
在 SwiftUI 中可以轻松构建条形图视图,接下来尝试使用带有示例数据的新 BarChartView,如下所示,创建了一组 DataPoint 实例的示例数组,并将其传递给 BarChartView,还为图表创建了一个可访问元素,并禁用了其子元素的可访问性信息。为了改进图表视图的可访问性体验,还添加了可访问性标签。
struct ContentView : View {
@ State private var dataPoints = [
DataPoint ( label: "1" , value: 3 , color: . red) ,
DataPoint ( label: "2" , value: 5 , color: . blue) ,
DataPoint ( label: "3" , value: 2 , color: . red) ,
DataPoint ( label: "4" , value: 4 , color: . blue) ,
]
var body: some View {
BarChartView ( dataPoints: dataPoints)
. accessibilityElement ( )
. accessibilityLabel ( "Chart representing some data" )
}
}
最后,可以开始为条形图视图实现音频图表功能,音频图表可以通过旋钮菜单获得。要使用旋钮,请在 iOS 设备的屏幕上旋转两个手指,就像拨盘。VoiceOver 会说出第一个旋钮选项,继续旋转手指以听到更多选项,松开手指选择音频图表,然后在屏幕上上下滑动手指以导航。 音频图表允许用户使用音频组件理解和解释图表数据,VoiceOver 在移动到图表视图中的条形时播放具有不同音调的声音。VoiceOver 对于更大的值使用高音调,对于较小的值使用低音调,这些音调代表数组中的数据。
四、实现协议
现在可以讨论在 BarChartView 中实现此功能的方法。首先必须创建一个符合 AXChartDescriptorRepresentable 协议的类型,AXChartDescriptorRepresentable 协议只有一个要求,即创建 AXChartDescriptor 类型的实例。AXChartDescriptor 类型的实例表示图表中的数据,以 VoiceOver 可以理解和交互的格式呈现。
extension ContentView : AXChartDescriptorRepresentable {
func makeChartDescriptor ( ) -> AXChartDescriptor {
let xAxis = AXCategoricalDataAxisDescriptor (
title: "Labels" ,
categoryOrder: dataPoints. map ( \. label)
)
let min = dataPoints. map ( \. value) . min ( ) ? ? 0.0
let max = dataPoints. map ( \. value) . max ( ) ? ? 0.0
let yAxis = AXNumericDataAxisDescriptor (
title: "Values" ,
range: min... max,
gridlinePositions: [ ]
) { value in "\(value) points" }
let series = AXDataSeriesDescriptor (
name: "" ,
isContinuous: false ,
dataPoints: dataPoints. map {
. init ( x: $0 . label, y: $0 . value)
}
)
return AXChartDescriptor (
title: "Chart representing some data" ,
summary: nil,
xAxis: xAxis,
yAxis: yAxis,
additionalAxes: [ ] ,
series: [ series]
)
}
}
我们所需做的就是符合 AXChartDescriptorRepresentable 协议,并添加 makeChartDescriptor 函数,该函数返回 AXChartDescriptor 的实例。首先,通过使用 AXCategoricalDataAxisDescriptor 和 AXNumericDataAxisDescriptor 类型定义 X 轴和 Y 轴,我们希望在 X 轴上使用字符串标签,这就是为什么使用 AXCategoricalDataAxisDescriptor 类型的原因。在线图的情况下,将在两个轴上都使用 AXNumericDataAxisDescriptor 类型。
五、实现线图
接下来,使用 AXDataSeriesDescriptor 类型定义图表中的点,有一个 isContinuous 参数,允许定义不同的图表样式。例如,对于条形图,它应该是 false,而对于线图,它应该是 true。
struct ContentView : View {
@ State private var dataPoints = [
DataPoint ( label: "1" , value: 3 , color: . red) ,
DataPoint ( label: "2" , value: 5 , color: . blue) ,
DataPoint ( label: "3" , value: 2 , color: . red) ,
DataPoint ( label: "4" , value: 4 , color: . blue) ,
]
var body: some View {
BarChartView ( dataPoints: dataPoints)
. accessibilityElement ( )
. accessibilityLabel ( "Chart representing some data" )
. accessibilityChartDescriptor ( self )
}
}
作为最后一步,使用 accessibilityChartDescriptor 视图修饰符将符合 AXChartDescriptorRepresentable 协议的实例设置为描述图表的实例。结果如下:
六、总结
音频图表功能对于视力受损的用户来说是一项重大改进。音频图表功能的好处是,可以将其用于任何想要的视图,甚至包括图像视图,只需创建 AXChartDescriptor 类型的实例。