SwiftUI:辅助功能——项目优化示例
优化猜国旗项目
在项目2 猜国旗项目中,我们制作了Guess the Flag,它显示了三个标志图片,并要求用户猜测是哪个。好吧,根据您现在对VoiceOver的了解,您可以发现我们游戏中的致命缺陷吗?
没错:SwiftUI的默认行为是读取图像名称作为其VoiceOver标签,这意味着使用VoiceOver的任何人都可以拿到我们的三个国旗的名字,然后选择正确的。
要解决此问题,我们需要为每个标志添加文本描述,并对其进行足够详细的描述,以使了解它们的人可以正确猜出它们,但是当然不必实际给出国家名称。
如果您打开此项目的副本,则会看到它是使用一组国家名称编写的,如下所示:
@State private var countries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Nigeria", "Poland", "Russia", "Spain", "UK", "US"].shuffled()
因此,在此处附加标签的最简单方法——不需要我们更改任何代码的方法——就是创建一个字典,以国家/地区名称作为键,将可访问性标签作为值,像这样。请将其添加到ContentView
:
let labels = [
"Estonia": "Flag with three horizontal stripes of equal size. Top stripe blue, middle stripe black, bottom stripe white",
"France": "Flag with three vertical stripes of equal size. Left stripe blue, middle stripe white, right stripe red",
"Germany": "Flag with three horizontal stripes of equal size. Top stripe black, middle stripe red, bottom stripe gold",
"Ireland": "Flag with three vertical stripes of equal size. Left stripe green, middle stripe white, right stripe orange",
"Italy": "Flag with three vertical stripes of equal size. Left stripe green, middle stripe white, right stripe red",
"Nigeria": "Flag with three vertical stripes of equal size. Left stripe green, middle stripe white, right stripe green",
"Poland": "Flag with two horizontal stripes of equal size. Top stripe white, bottom stripe red",
"Russia": "Flag with three horizontal stripes of equal size. Top stripe white, middle stripe blue, bottom stripe red",
"Spain": "Flag with three horizontal stripes. Top thin stripe red, middle thick stripe gold with a crest on the left, bottom thin stripe red",
"UK": "Flag with overlapping red and white crosses, both straight and diagonally, on a blue background",
"US": "Flag with red and white stripes of equal size, with white stars on a blue background in the top-left corner"
]
现在我们需要做的就是将accessibility(label:)
修饰符添加到标志图像。我意识到这听起来很简单,但是代码必须要做四件事:
- 使用
self.countries [number]
获取当前标志的国家/地区名称。 - 使用该名称作为
self.labels
的键。 - 如果国家/地区名称在字典中不存在,请提供一个字符串作为默认值。(这永远都不会发生,但是安全无害!)
- 将最终字符串转换为文本视图。
组合这四点在一起,将这个修饰符直接放在标记图像其余修饰符的下面:
.accessibility(label: Text(self.labels[self.countries[number], default: "Unknown flag"]))
现在,如果您再次运行游戏,则无论您是否使用VoiceOver,您都会看到它实际上是一个游戏。这就是无障碍访问的核心:无论访问需求如何,每个人都可以立即玩此游戏。
优化Word Scramble 项目
在项目5中,我们构建了Word Scramble,该游戏向用户提供了一个随机的8个字母的单词,并且不得不使用其字母生成新单词。这通常可以在VoiceOver上很好地发挥作用:应用程序的任何部分都无法访问,尽管这并不意味着我们无法做得更好。
要查看明显的痛点,请尝试添加一个单词。您会看到它滑到提示下方的表格中,但是如果您使用VoiceOver轻敲它,您会发现它的阅读效果不佳:字母计数被读为“五个圆圈”,而文本是一个单独的元素。
有几种方法可以改善此问题,但最好的办法是将这两个项目都设为一个单独的组,让VoiceOver忽略这些内容,然后为整个组添加一个包含更自然描述的标签。
我们当前的代码如下:
List(usedWords, id: \.self) {
Image(systemName: "\($0.count).circle")
Text($0)
}
这依赖于隐式HStack
来将图像和文本并排放置。因此,要解决此问题,我们需要创建一个显式的HStack
,以便我们可以应用VoiceOver定制。HStack
本身接受其内容的闭包,这意味着我们不再可以依赖列表中的$0
,因此我们将使用命名参数。
将当前列表替换为:
List(usedWords, id: \.self) { word in
HStack {
Image(systemName: "\(word.count).circle")
Text(word)
}
.accessibilityElement(children: .ignore)
.accessibility(label: Text("\(word), \(word.count) 个字母"))
}
如果您再次尝试游戏,您会看到它现在显示为“ spill,五个字母”,这要好得多。
优化 Bookworm 项目
在项目11中,我们构建了Bookworm,这是一个应用程序,可让用户存储已阅读书籍的评分和描述,我们还引入了一个自定义的RatingView
UI组件,该组件显示的星级为1到5。
再说一次,大多数应用程序在VoiceOver上都运行良好,但是评级控制是一个失败的尝试——它使用点击手势来添加功能,因此用户不会意识到它们是按钮,甚至无法传达它们是按钮的事实。应该代表评级。例如,如果我点击其中一个灰色的星标,VoiceOver会向我读出“星标,填充,图像,可能是飞机,星标”的信息——真的没有用。
这本身就是一个问题,但是这又是一个额外的问题,因为我们的RatingView
设计为可重用的——这是您可以从该项目中获取的东西,并可以在其他十二个项目中使用,这仅意味着您最终污染了许多应用程序的可访问性。
我们可以使用三个修饰符来解决此问题,每个修饰符都添加到RatingView
中当前的tapGesture()
修饰符下方。首先,我们需要添加一个为每颗星星提供有意义的标签,如下所示:
.accessibility(label: Text("\(number == 1 ? "1 star" : "\(number) stars")"))
其次,我们可以删除.isImage
特征,因为这些是图像并不重要:
.accessibility(removeTraits: .isImage)
最后,我们应该告诉系统每个星星实际上都是一个按钮,以便用户知道可以点击它。当我们在这里时,对于已经高亮的星星,则可以通过添加第二个特征.isSelected
来使VoiceOver的工作做得更好。
因此,在最后两个下面添加最终修饰符:
.accessibility(addTraits: number > self.rating ? .isButton : [.isButton, .isSelected])
仅进行了三个小更改,但是此改进的组件比我们以前拥有的要好得多。
译自
Fixing Guess the Flag
Fixing Word Scramble
Fixing Bookworm