WWDC Quick Look 💓 By SwiftGGTeam
Developer spotlight: Accessibility

Developer spotlight: Accessibility

观看原视频

Highlight

Apple 通过视障和听障开发者的真实案例说明,无障碍改进可以从按钮标签、VoiceOver 测试和多感官反馈开始,让同一个 App 被更多用户和开发者使用。

核心内容

很多团队把无障碍放到发布前检查。界面做完以后,再打开 VoiceOver 试一下。发现按钮读不出来、图片没有说明、交互依赖声音,就把它们列成以后再修的任务。

这场演讲给出的痛点更具体。一位开发者提到,有个 App 的图形按钮都没有标签,VoiceOver 用户无法知道按钮含义。开发者补上标签后,只用一天时间,App 就变得基本可访问。

Apple 没有把这场演讲做成 API 发布。它让视障和听障开发者讲自己的工作方式。听障开发者会自然检查 App 在没有声音时能否运行。视障开发者每天用屏幕阅读器写代码、测试 App、提交 bug。

这改变了无障碍的起点。开发者不用先设计一套单独版本。先做同一个版本,让按钮有名称,让 VoiceOver 能读出状态,让声音提示有可视替代,让视觉任务有音频引导。

从一个按钮标签开始

一个完全不可访问的 App,问题可能很小:图形按钮没有标签。屏幕阅读器只能读出“按钮”,用户不知道下一步会发生什么。

补上标签后,用户就能理解界面。这个改动不需要重写架构。它要求开发者把控件的语义交给系统。

工具本身也要可访问

听障开发者收到 VoiceOver bug 时,问题变成:如果听不到 VoiceOver 朗读,怎么测试?

答案是 VoiceOver 的字幕栏(caption bar)。它把 VoiceOver 朗读内容显示成文字,例如 “Tweets and replies, Button”。这个小功能让听障开发者也能调试 VoiceOver 问题。

从个人痛点做产品

Seeing AI 来自视障开发者的个人需要。它描述周围世界,读取文字,识别人和物体。扫描条形码时,团队用“热或冷”的方式提供音频引导:越接近目标,蜂鸣越快。

Cardzilla 来自听障开发者的打车经历。他原来用 Notes App 和司机沟通,但文字太小。于是做了一个把文字放大的 App,让对方更容易读,也让沟通更快。

详细内容

1. 给图形按钮提供可读标签

02:05)演讲中的第一个具体案例是按钮标签。某个 App 因为图形按钮没有标签,VoiceOver 用户无法使用。开发者补上标签后,App 变得基本可访问。

SwiftUI 中,图形按钮可以用 accessibilityLabel 提供语义名称。下面是一个完整的最小示例。

import SwiftUI

struct PlaybackControls: View {
    @State private var isPlaying = false

    var body: some View {
        Button {
            isPlaying.toggle()
        } label: {
            Image(systemName: isPlaying ? "pause.fill" : "play.fill")
        }
        .accessibilityLabel(isPlaying ? "Pause" : "Play")
    }
}

struct PlaybackControls_Previews: PreviewProvider {
    static var previews: some View {
        PlaybackControls()
    }
}

关键点

  • import SwiftUI 引入 SwiftUI,示例可以直接放进 SwiftUI App 或预览中运行。
  • @State private var isPlaying = false 保存播放状态,按钮点击后会更新界面。
  • Button { ... } label: { ... } 创建一个只有图标的按钮。
  • Image(systemName:) 根据状态显示播放或暂停图标。
  • .accessibilityLabel(isPlaying ? "Pause" : "Play") 给图形按钮提供 VoiceOver 可朗读的名称。
  • PlaybackControls_Previews 提供 Xcode 预览入口,便于打开 Accessibility Preview 检查。

2. 用同一个版本服务所有用户

01:43)Avid 的开发者说,目标不是做一个“盲人版 Pro Tools”。目标是一个所有人都能走上前使用的版本。

这个原则落到代码里,通常是把状态、动作和标签放在原有控件上,让系统辅助功能读取同一套界面。下面的示例把一个切换静音的按钮做成同一个控件,不额外复制一套无障碍界面。

import SwiftUI

struct MuteButton: View {
    @State private var isMuted = false

    var body: some View {
        Button {
            isMuted.toggle()
        } label: {
            Label(isMuted ? "Unmute" : "Mute",
                  systemImage: isMuted ? "speaker.slash.fill" : "speaker.wave.2.fill")
        }
        .accessibilityValue(isMuted ? "Muted" : "Sound on")
    }
}

struct MuteButton_Previews: PreviewProvider {
    static var previews: some View {
        MuteButton()
    }
}

关键点

  • @State private var isMuted = false 保存当前声音状态。
  • Button 是所有用户共用的交互入口。
  • Label 同时包含文字和系统图标,视觉用户和 VoiceOver 用户都能获得名称。
  • systemImage 随状态变化,视觉界面能表达静音状态。
  • .accessibilityValue 朗读当前值,让用户知道按钮现在对应“Muted”还是“Sound on”。

3. 让没有声音的用户也能检查 VoiceOver

03:03)一位听障开发者收到 VoiceOver bug。他发现 VoiceOver 有字幕栏,可以把朗读内容显示成文字。演讲中的字幕栏显示了 “Tweets and replies, Button”“Media, Button”“Likes, Button”。

应用开发时,可以同时准备可朗读的标签和可见文本。这样听障测试者可以通过字幕栏核对 VoiceOver 输出,视障用户可以通过屏幕阅读器听到同一份语义。

import SwiftUI

struct ProfileTabs: View {
    var body: some View {
        HStack {
            Button("Tweets and replies") {}
            Button("Media") {}
            Button("Likes") {}
        }
        .padding()
    }
}

struct ProfileTabs_Previews: PreviewProvider {
    static var previews: some View {
        ProfileTabs()
    }
}

关键点

  • HStack 横向排列三个按钮,对应演讲字幕栏中的三个可聚焦元素。
  • Button("Tweets and replies") {} 使用文字标题,VoiceOver 可以直接朗读。
  • Button("Media") {}Button("Likes") {} 保持同样模式,减少遗漏标签的机会。
  • .padding() 只影响视觉布局,不改变无障碍语义。
  • 运行时打开 VoiceOver 字幕栏,测试者可以用文字核对朗读结果。

4. 为摄像头任务提供音频引导

03:59)Seeing AI 团队发现,扫描条形码时用户需要知道相机是否对准目标。团队使用“热或冷”的游戏规则:越接近条形码,蜂鸣越快。

这个思路适合所有需要用户对准目标的任务。代码可以先把“接近程度”抽象成一个 0 到 1 的值,再把它转换成蜂鸣间隔。

import Foundation

struct BarcodeAudioGuide {
    func beepInterval(for proximity: Double) -> TimeInterval {
        let clampedProximity = min(max(proximity, 0), 1)
        let slowestInterval = 1.0
        let fastestInterval = 0.15
        return slowestInterval - (slowestInterval - fastestInterval) * clampedProximity
    }
}

let guide = BarcodeAudioGuide()
let interval = guide.beepInterval(for: 0.75)
print(interval)

关键点

  • import Foundation 引入 TimeInterval
  • BarcodeAudioGuide 保存条形码音频引导的计算逻辑。
  • beepInterval(for:) 接收接近程度,0 表示远,1 表示近。
  • min(max(proximity, 0), 1) 把输入限制在 0 到 1,避免异常值影响计算。
  • slowestInterval 是最慢蜂鸣间隔。
  • fastestInterval 是最快蜂鸣间隔。
  • return 根据接近程度缩短间隔,越接近目标,返回值越小。
  • print(interval) 让示例可以直接运行并观察结果。

5. 把文字放大,解决面对面沟通

04:27)Cardzilla 的起点很日常:听障开发者坐出租车时,用 Notes App 给司机看文字,但文字太小。于是他做了一个放大文字、提高沟通效率的 App。

这个功能不复杂。关键是让输入内容立刻变成大字号、居中、便于对方阅读。

import SwiftUI

struct LargeMessageCard: View {
    @State private var message = "Please take me home."

    var body: some View {
        VStack(spacing: 24) {
            TextEditor(text: $message)
                .frame(height: 120)
                .border(Color.secondary)

            Text(message)
                .font(.system(size: 56, weight: .bold))
                .multilineTextAlignment(.center)
                .minimumScaleFactor(0.4)
                .padding()
        }
        .padding()
    }
}

struct LargeMessageCard_Previews: PreviewProvider {
    static var previews: some View {
        LargeMessageCard()
    }
}

关键点

  • @State private var message 保存要展示给对方看的文字。
  • TextEditor(text: $message) 允许用户编辑内容。
  • .frame(height: 120) 限制输入区高度,让展示区保留空间。
  • Text(message) 显示放大后的内容。
  • .font(.system(size: 56, weight: .bold)) 使用大字号和粗体,提高远距离可读性。
  • .multilineTextAlignment(.center) 让多行文字居中。
  • .minimumScaleFactor(0.4) 在文字过长时自动缩小,减少截断。

核心启发

  1. 做什么:给所有图标按钮补充可朗读名称。 为什么值得做:演讲中的案例显示,缺少按钮标签会让 App 完全不可访问;补上标签后,一天内就能让 App 基本可用。 怎么开始:打开常用页面,逐个检查只有图标的 Button,在 SwiftUI 中添加 .accessibilityLabel,在 UIKit 中设置 accessibilityLabel

  2. 做什么:给语音和声音提示增加可视检查路径。 为什么值得做:听障开发者依靠 VoiceOver 字幕栏调试 VoiceOver bug,说明测试工具和调试信息需要跨感官呈现。 怎么开始:打开 VoiceOver 字幕栏,核对每个可聚焦元素读出的文字;对依赖音频的状态提示增加文字、图标或震动反馈。

  3. 做什么:为摄像头扫描增加“越接近越密集”的反馈。 为什么值得做:Seeing AI 的条形码扫描用蜂鸣频率帮助用户对准目标,解决了看不到取景画面时的操作问题。 怎么开始:先计算目标在画面中的置信度或距离,把它映射成蜂鸣间隔、震动节奏或屏幕提示。

  4. 做什么:做一个面对面大字沟通模式。 为什么值得做:Cardzilla 的场景来自出租车沟通,核心需求是让对方快速读懂文字。 怎么开始:用 TextEditor 输入内容,用大字号 Text 全屏展示,再加入语音输入入口。

  5. 做什么:把残障用户加入 beta 测试流程。 为什么值得做:演讲提到,无障碍需要公司内部和外部 beta 测试者的输入;团队多样性会带来更直接的问题发现。 怎么开始:在 TestFlight 招募说明中明确欢迎 VoiceOver、字幕、键盘访问、Switch Control 用户反馈,并把反馈归类到具体页面和控件。

关联 Session

评论

GitHub Issues · utterances