axiom-accessibility-diag

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Accessibility Diagnostics

无障碍诊断

Overview

概述

Systematic accessibility diagnosis and remediation for iOS/macOS apps. Covers the 7 most common accessibility issues that cause App Store rejections and user complaints.
Core principle Accessibility is not optional. iOS apps must support VoiceOver, Dynamic Type, and sufficient color contrast to pass App Store Review. Users with disabilities depend on these features.
为iOS/macOS应用提供系统化的无障碍问题诊断与修复方案。涵盖导致App Store审核拒绝和用户投诉的7类最常见无障碍问题。
核心原则 无障碍支持并非可选功能。iOS应用必须支持VoiceOver、Dynamic Type,并具备足够的色彩对比度才能通过App Store审核。残障用户依赖这些功能正常使用应用。

When to Use This Skill

何时使用本技能

  • Fixing VoiceOver navigation issues (missing labels, wrong element order)
  • Supporting Dynamic Type (text scaling for vision disabilities)
  • Meeting color contrast requirements (WCAG AA/AAA)
  • Fixing touch target size violations (< 44x44pt)
  • Adding keyboard navigation (iPadOS/macOS)
  • Supporting Reduce Motion (vestibular disorders)
  • Preparing for App Store Review accessibility requirements
  • Responding to user complaints about accessibility
  • 修复VoiceOver导航问题(缺失标签、元素顺序错误)
  • 支持Dynamic Type(为视障用户提供文本缩放功能)
  • 满足色彩对比度要求(WCAG AA/AAA标准)
  • 修复触摸目标尺寸违规问题(小于44x44pt)
  • 添加键盘导航支持(iPadOS/macOS)
  • 支持Reduce Motion(为前庭障碍用户优化)
  • 为App Store审核的无障碍要求做准备
  • 响应用户关于无障碍体验的投诉

The 7 Critical Accessibility Issues

7类关键无障碍问题

1. VoiceOver Labels & Hints (CRITICAL - App Store Rejection)

1. VoiceOver标签与提示(严重问题 - 会导致App Store审核拒绝)

Problem Missing or generic accessibility labels prevent VoiceOver users from understanding UI purpose.
WCAG 4.1.2 Name, Role, Value (Level A)
问题 缺失或通用的无障碍标签会导致VoiceOver用户无法理解UI元素的用途。
WCAG标准 4.1.2 名称、角色、值(A级要求)

Common violations

常见违规示例

swift
// ❌ WRONG - No label (VoiceOver says "Button")
Button(action: addToCart) {
  Image(systemName: "cart.badge.plus")
}

// ❌ WRONG - Generic label
.accessibilityLabel("Button")

// ❌ WRONG - Reads implementation details
.accessibilityLabel("cart.badge.plus") // VoiceOver: "cart dot badge dot plus"

// ✅ CORRECT - Descriptive label
Button(action: addToCart) {
  Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Add to cart")

// ✅ CORRECT - With hint for complex actions
.accessibilityLabel("Add to cart")
.accessibilityHint("Double-tap to add this item to your shopping cart")
swift
// ❌ 错误 - 未设置标签(VoiceOver会播报“按钮”)
Button(action: addToCart) {
  Image(systemName: "cart.badge.plus")
}

// ❌ 错误 - 使用通用标签
.accessibilityLabel("Button")

// ❌ 错误 - 播报实现细节
.accessibilityLabel("cart.badge.plus") // VoiceOver会播报:“cart dot badge dot plus”

// ✅ 正确 - 描述性标签
Button(action: addToCart) {
  Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Add to cart")

// ✅ 正确 - 为复杂操作添加提示
.accessibilityLabel("Add to cart")
.accessibilityHint("Double-tap to add this item to your shopping cart")

When to use hints

何时使用提示

  • Action is not obvious from label ("Add to cart" is obvious, no hint needed)
  • Multi-step interaction ("Swipe right to confirm, left to cancel")
  • State change ("Double-tap to toggle notifications on or off")
  • 操作无法通过标签直接体现(“加入购物车”含义明确,无需提示)
  • 多步骤交互(“向右滑动确认,向左滑动取消”)
  • 状态切换(“双击可开启或关闭通知”)

Decorative elements

装饰性元素处理

swift
// ✅ CORRECT - Hide decorative images from VoiceOver
Image("decorative-pattern")
  .accessibilityHidden(true)

// ✅ CORRECT - Combine multiple elements into one label
HStack {
  Image(systemName: "star.fill")
  Text("4.5")
  Text("(234 reviews)")
}
.accessibilityElement(children: .combine)
.accessibilityLabel("Rating: 4.5 stars from 234 reviews")
swift
// ✅ 正确 - 隐藏装饰性图片,避免被VoiceOver识别
Image("decorative-pattern")
  .accessibilityHidden(true)

// ✅ 正确 - 将多个元素合并为一个标签
HStack {
  Image(systemName: "star.fill")
  Text("4.5")
  Text("(234 reviews)")
}
.accessibilityElement(children: .combine)
.accessibilityLabel("Rating: 4.5 stars from 234 reviews")

Testing

测试方法

  • Enable VoiceOver: Cmd+F5 (simulator) or triple-click side button (device)
  • Navigate: Swipe right/left to move between elements
  • Listen: Does VoiceOver announce purpose clearly?
  • Check order: Does navigation order match visual layout?

  • 启用VoiceOver:模拟器使用Cmd+F5,设备使用三击侧边按钮(需在设置中开启)
  • 导航:左右滑动切换元素
  • 聆听:VoiceOver是否清晰播报元素用途?
  • 检查顺序:导航顺序是否与视觉布局一致?

2. Dynamic Type Support (HIGH - User Experience)

2. Dynamic Type支持(高优先级 - 用户体验优化)

Problem Fixed font sizes prevent users with vision disabilities from reading text.
WCAG 1.4.4 Resize Text (Level AA - support 200% scaling without loss of content/functionality)
问题 固定字体大小会导致视障用户无法正常阅读文本。
WCAG标准 1.4.4 文本缩放(AA级要求 - 支持200%文本缩放且不丢失内容或功能)

Common violations

常见违规示例

swift
// ❌ WRONG - Fixed size, won't scale
Text("Price: $19.99")
  .font(.system(size: 17))

UILabel().font = UIFont.systemFont(ofSize: 17)

// ❌ WRONG - Custom font without scaling
Text("Headline")
  .font(Font.custom("CustomFont", size: 24))

// ✅ CORRECT - SwiftUI semantic styles (auto-scales)
Text("Price: $19.99")
  .font(.body)

Text("Headline")
  .font(.headline)

// ✅ CORRECT - UIKit semantic styles
label.font = UIFont.preferredFont(forTextStyle: .body)

// ✅ CORRECT - Custom font with scaling
let customFont = UIFont(name: "CustomFont", size: 24)!
label.font = UIFontMetrics.default.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true
swift
// ❌ 错误 - 固定大小,无法缩放
Text("Price: $19.99")
  .font(.system(size: 17))

UILabel().font = UIFont.systemFont(ofSize: 17)

// ❌ 错误 - 自定义字体未支持缩放
Text("Headline")
  .font(Font.custom("CustomFont", size: 24))

// ✅ 正确 - SwiftUI语义化样式(自动缩放)
Text("Price: $19.99")
  .font(.body)

Text("Headline")
  .font(.headline)

// ✅ 正确 - UIKit语义化样式
label.font = UIFont.preferredFont(forTextStyle: .body)

// ✅ 正确 - 支持缩放的自定义字体
let customFont = UIFont(name: "CustomFont", size: 24)!
label.font = UIFontMetrics.default.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true

Custom sizes that scale with Dynamic Type

随Dynamic Type缩放的自定义尺寸

swift
// ❌ WRONG - Fixed size, won't scale
Text("Price: $19.99")
  .font(.system(size: 17))

// ⚠️ ACCEPTABLE - Custom font without scaling (accessibility violation)
Text("Headline")
  .font(Font.custom("CustomFont", size: 24))

// ✅ GOOD - Custom size that scales with Dynamic Type
Text("Large Title")
  .font(.system(size: 60).relativeTo(.largeTitle))

Text("Custom Headline")
  .font(.system(size: 24).relativeTo(.title2))

// ✅ BEST - Use semantic styles when possible
Text("Headline")
  .font(.headline)
How
relativeTo:
works
  • Base size: Your exact pixel size (24pt, 60pt, etc.)
  • Scales with: The text style you specify (
    .title2
    ,
    .largeTitle
    , etc.)
  • Result: When user increases text size in Settings, your custom size grows proportionally
Example
  • .title2
    base: ~22pt → Your custom: 24pt (1.09x larger)
  • User increases to "Extra Large" text
  • .title2
    grows to ~28pt → Your custom grows to ~30.5pt (maintains 1.09x ratio)
Fix hierarchy (best to worst)
  1. Best: Use semantic styles (
    .title
    ,
    .body
    ,
    .caption
    )
  2. Good: Use
    .system(size:).relativeTo()
    for required custom sizes
  3. Acceptable: Custom font with
    .dynamicTypeSize()
    modifier
  4. Unacceptable: Fixed sizes that never scale
swift
// ❌ 错误 - 固定大小,无法缩放
Text("Price: $19.99")
  .font(.system(size: 17))

// ⚠️ 可接受 - 自定义字体未支持缩放(违反无障碍要求)
Text("Headline")
  .font(Font.custom("CustomFont", size: 24))

// ✅ 推荐 - 随Dynamic Type缩放的自定义尺寸
Text("Large Title")
  .font(.system(size: 60).relativeTo(.largeTitle))

Text("Custom Headline")
  .font(.system(size: 24).relativeTo(.title2))

// ✅ 最佳实践 - 尽可能使用语义化样式
Text("Headline")
  .font(.headline)
relativeTo:
的工作原理
  • 基础尺寸:你设置的具体像素大小(如24pt、60pt等)
  • 缩放依据:你指定的文本样式(如
    .title2
    .largeTitle
    等)
  • 效果:当用户在设置中增大文本尺寸时,你的自定义尺寸会按比例放大
示例
  • .title2
    基础尺寸:约22pt → 你的自定义尺寸:24pt(是基础的1.09倍)
  • 用户将文本调整为“超大”尺寸
  • .title2
    变为约28pt → 你的自定义尺寸变为约30.5pt(保持1.09倍比例)
修复优先级(从优到劣)
  1. 最佳:使用语义化样式(
    .title
    .body
    .caption
  2. 推荐:对必须自定义的尺寸使用
    .system(size:).relativeTo()
  3. 可接受:自定义字体搭配
    .dynamicTypeSize()
    修饰符
  4. 不可接受:使用完全固定的、无法缩放的字体尺寸

SwiftUI text styles

SwiftUI文本样式

  • .largeTitle
    - 34pt (scales to 44pt at accessibility sizes)
  • .title
    - 28pt
  • .title2
    - 22pt
  • .title3
    - 20pt
  • .headline
    - 17pt semibold
  • .body
    - 17pt (default)
  • .callout
    - 16pt
  • .subheadline
    - 15pt
  • .footnote
    - 13pt
  • .caption
    - 12pt
  • .caption2
    - 11pt
  • .largeTitle
    - 34pt(无障碍尺寸下会缩放至44pt)
  • .title
    - 28pt
  • .title2
    - 22pt
  • .title3
    - 20pt
  • .headline
    - 17pt 粗体
  • .body
    - 17pt(默认)
  • .callout
    - 16pt
  • .subheadline
    - 15pt
  • .footnote
    - 13pt
  • .caption
    - 12pt
  • .caption2
    - 11pt

Layout considerations

布局注意事项

swift
// ❌ WRONG - Fixed frame breaks with large text
Text("Long product description...")
  .font(.body)
  .frame(height: 50) // Clips at large text sizes

// ✅ CORRECT - Flexible frame
Text("Long product description...")
  .font(.body)
  .lineLimit(nil) // Allow multiple lines
  .fixedSize(horizontal: false, vertical: true)

// ✅ CORRECT - Stack rearranges at large sizes
HStack {
  Text("Label:")
  Text("Value")
}
.dynamicTypeSize(...DynamicTypeSize.xxxLarge) // Limit maximum size if needed
swift
// ❌ 错误 - 固定高度会导致大文本被截断
Text("Long product description...")
  .font(.body)
  .frame(height: 50) // 大文本尺寸下会被裁剪

// ✅ 正确 - 灵活布局
Text("Long product description...")
  .font(.body)
  .lineLimit(nil) // 允许多行显示
  .fixedSize(horizontal: false, vertical: true)

// ✅ 正确 - 大尺寸下自动重排的布局
HStack {
  Text("Label:")
  Text("Value")
}
.dynamicTypeSize(...DynamicTypeSize.xxxLarge) // 可根据需要限制最大尺寸

Testing

测试方法

  1. Xcode Preview: Environment override
    swift
    .environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge)
  2. Simulator: Settings → Accessibility → Display & Text Size → Larger Text → Drag to maximum
  3. Device: Settings → Accessibility → Display & Text Size → Larger Text
  4. Check: Does text remain readable? Does layout adapt? Is any text clipped?

  1. Xcode预览:环境变量覆盖
    swift
    .environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge)
  2. 模拟器:设置 → 无障碍 → 显示与文本大小 → 更大字体 → 拖动到最大
  3. 设备:设置 → 无障碍 → 显示与文本大小 → 更大字体
  4. 检查:文本是否仍可读?布局是否自适应?是否有文本被截断?

3. Color Contrast (HIGH - Vision Disabilities)

3. 色彩对比度(高优先级 - 视障用户适配)

Problem Low contrast text is unreadable for users with vision disabilities or in bright sunlight.
问题 低对比度文本对视障用户或在强光环境下难以阅读。

WCAG

WCAG标准

  • 1.4.3 Contrast (Minimum) — Level AA
    • Normal text (< 18pt): 4.5:1 contrast ratio
    • Large text (≥ 18pt or ≥ 14pt bold): 3:1 contrast ratio
  • 1.4.6 Contrast (Enhanced) — Level AAA
    • Normal text: 7:1 contrast ratio
    • Large text: 4.5:1 contrast ratio
  • 1.4.3 最低对比度 — AA级
    • 普通文本(<18pt):4.5:1的对比度
    • 大文本(≥18pt 或 ≥14pt粗体):3:1的对比度
  • 1.4.6 增强对比度 — AAA级
    • 普通文本:7:1的对比度
    • 大文本:4.5:1的对比度

Common violations

常见违规示例

swift
// ❌ WRONG - Low contrast (1.8:1 - fails WCAG)
Text("Warning")
  .foregroundColor(.yellow) // on white background

// ❌ WRONG - Low contrast in dark mode
Text("Info")
  .foregroundColor(.gray) // on black background

// ✅ CORRECT - High contrast (7:1+ passes AAA)
Text("Warning")
  .foregroundColor(.orange) // or .red

// ✅ CORRECT - System colors adapt to light/dark mode
Text("Info")
  .foregroundColor(.primary) // Black in light mode, white in dark

Text("Secondary")
  .foregroundColor(.secondary) // Automatic high contrast
swift
// ❌ 错误 - 低对比度(1.8:1 - 不符合WCAG标准)
Text("Warning")
  .foregroundColor(.yellow) // 白色背景下

// ❌ 错误 - 深色模式下对比度不足
Text("Info")
  .foregroundColor(.gray) // 黑色背景下

// ✅ 正确 - 高对比度(7:1+ 符合AAA级)
Text("Warning")
  .foregroundColor(.orange) // 或红色

// ✅ 正确 - 系统颜色自动适配明暗模式
Text("Info")
  .foregroundColor(.primary) // 亮色模式为黑色,暗色模式为白色

Text("Secondary")
  .foregroundColor(.secondary) // 自动保持高对比度

Differentiate Without Color

不依赖颜色区分状态

swift
// ❌ WRONG - Color alone indicates status
Circle()
  .fill(isAvailable ? .green : .red)

// ✅ CORRECT - Color + icon/text
HStack {
  Image(systemName: isAvailable ? "checkmark.circle.fill" : "xmark.circle.fill")
  Text(isAvailable ? "Available" : "Unavailable")
}
.foregroundColor(isAvailable ? .green : .red)

// ✅ CORRECT - Respect system preference
if UIAccessibility.shouldDifferentiateWithoutColor {
  // Use patterns, icons, or text instead of color alone
}
swift
// ❌ 错误 - 仅用颜色表示状态
Circle()
  .fill(isAvailable ? .green : .red)

// ✅ 正确 - 颜色+图标/文本
HStack {
  Image(systemName: isAvailable ? "checkmark.circle.fill" : "xmark.circle.fill")
  Text(isAvailable ? "Available" : "Unavailable")
}
.foregroundColor(isAvailable ? .green : .red)

// ✅ 正确 - 遵循系统偏好设置
if UIAccessibility.shouldDifferentiateWithoutColor {
  // 使用图案、图标或文本替代单一颜色
}

Testing

测试方法

  1. Use Color Contrast Analyzer tool (free download)
  2. Screenshot your UI, measure text vs background
  3. Check both light and dark mode
  4. Settings → Accessibility → Display & Text Size → Increase Contrast (test with this ON)
  1. 使用色彩对比度分析工具(免费下载)
  2. 截取UI截图,测量文本与背景的对比度
  3. 同时检查亮色和暗色模式
  4. 设置 → 无障碍 → 显示与文本大小 → 增强对比度(开启后测试)

Quick reference

快速参考

  • Black (#000000) on White (#FFFFFF): 21:1 ✅ AAA
  • Dark Gray (#595959) on White: 7:1 ✅ AAA
  • Medium Gray (#767676) on White: 4.5:1 ✅ AA
  • Light Gray (#959595) on White: 2.8:1 ❌ Fails

  • 黑色(#000000)在白色(#FFFFFF)上:21:1 ✅ AAA级
  • 深灰色(#595959)在白色上:7:1 ✅ AAA级
  • 中灰色(#767676)在白色上:4.5:1 ✅ AA级
  • 浅灰色(#959595)在白色上:2.8:1 ❌ 不达标

4. Touch Target Sizes (MEDIUM - Motor Disabilities)

4. 触摸目标尺寸(中优先级 - 行动障碍用户适配)

Problem Small tap targets are difficult or impossible for users with motor disabilities.
WCAG 2.5.5 Target Size (Level AAA - 44x44pt minimum)
Apple HIG 44x44pt minimum for all tappable elements
问题 过小的点击目标会导致行动障碍用户难以操作。
WCAG标准 2.5.5 目标尺寸(AAA级 - 最小44x44pt)
Apple人机交互指南 所有可点击元素的最小尺寸为44x44pt

Common violations

常见违规示例

swift
// ❌ WRONG - Too small (24x24pt)
Button("×") {
  dismiss()
}
.frame(width: 24, height: 24)

// ❌ WRONG - Small icon without padding
Image(systemName: "heart")
  .font(.system(size: 16))
  .onTapGesture { }

// ✅ CORRECT - Minimum 44x44pt
Button("×") {
  dismiss()
}
.frame(minWidth: 44, minHeight: 44)

// ✅ CORRECT - Larger icon or padding
Image(systemName: "heart")
  .font(.system(size: 24))
  .frame(minWidth: 44, minHeight: 44)
  .contentShape(Rectangle()) // Expand tap area
  .onTapGesture { }

// ✅ CORRECT - UIKit button with edge insets
button.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
// Total size: icon size + insets ≥ 44x44pt
swift
// ❌ 错误 - 尺寸过小(24x24pt)
Button("×") {
  dismiss()
}
.frame(width: 24, height: 24)

// ❌ 错误 - 小图标未添加内边距
Image(systemName: "heart")
  .font(.system(size: 16))
  .onTapGesture { }

// ✅ 正确 - 最小44x44pt
Button("×") {
  dismiss()
}
.frame(minWidth: 44, minHeight: 44)

// ✅ 正确 - 放大图标或添加内边距
Image(systemName: "heart")
  .font(.system(size: 24))
  .frame(minWidth: 44, minHeight: 44)
  .contentShape(Rectangle()) // 扩大点击区域
  .onTapGesture { }

// ✅ 正确 - UIKit按钮添加内边距
button.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
// 总尺寸:图标尺寸 + 内边距 ≥44x44pt

Spacing between targets

目标间距

swift
// ❌ WRONG - Targets too close (hard to tap accurately)
HStack(spacing: 4) {
  Button("Edit") { }
  Button("Delete") { }
}

// ✅ CORRECT - Adequate spacing (8pt minimum, 12pt better)
HStack(spacing: 12) {
  Button("Edit") { }
  Button("Delete") { }
}
swift
// ❌ 错误 - 目标间距过近(难以准确点击)
HStack(spacing: 4) {
  Button("Edit") { }
  Button("Delete") { }
}

// ✅ 正确 - 足够的间距(最小8pt,12pt更佳)
HStack(spacing: 12) {
  Button("Edit") { }
  Button("Delete") { }
}

Testing

测试方法

  1. Accessibility Inspector: Xcode → Open Developer Tool → Accessibility Inspector
  2. Select "Audit" tab → Run audit → Check for "Small Text" and "Hit Region" warnings
  3. Manual: Tap with one finger (not stylus) — can you hit it reliably without mistakes?

  1. Accessibility Inspector:Xcode → 打开开发者工具 → Accessibility Inspector
  2. 选择“审核”标签 → 运行审核 → 检查“小文本”和“点击区域”警告
  3. 手动测试:用一根手指点击(不要用触控笔)——能否可靠点击而不失误?

5. Keyboard Navigation (MEDIUM - iPadOS/macOS)

5. 键盘导航(中优先级 - iPadOS/macOS适配)

Problem Users who cannot use touch/mouse cannot navigate app.
WCAG 2.1.1 Keyboard (Level A - all functionality available via keyboard)
问题 无法使用触摸/鼠标的用户无法导航应用。
WCAG标准 2.1.1 键盘操作(A级要求 - 所有功能均可通过键盘实现)

Common violations

常见违规示例

swift
// ❌ WRONG - Custom gesture without keyboard alternative
.onTapGesture {
  showDetails()
}
// No way to trigger with keyboard

// ✅ CORRECT - Button provides keyboard support automatically
Button("Show Details") {
  showDetails()
}
.keyboardShortcut("d", modifiers: .command) // Optional shortcut

// ✅ CORRECT - Custom control with focus support
struct CustomButton: View {
  @FocusState private var isFocused: Bool

  var body: some View {
    Text("Custom")
      .focusable()
      .focused($isFocused)
      .onKeyPress(.return) {
        action()
        return .handled
      }
  }
}
swift
// ❌ 错误 - 自定义手势未提供键盘替代方案
.onTapGesture {
  showDetails()
}
// 无法通过键盘触发

// ✅ 正确 - Button自动支持键盘操作
Button("Show Details") {
  showDetails()
}
.keyboardShortcut("d", modifiers: .command) // 可选快捷键

// ✅ 正确 - 支持焦点的自定义控件
struct CustomButton: View {
  @FocusState private var isFocused: Bool

  var body: some View {
    Text("Custom")
      .focusable()
      .focused($isFocused)
      .onKeyPress(.return) {
        action()
        return .handled
      }
  }
}

Focus management

焦点管理

swift
// ✅ CORRECT - Set initial focus
.focusSection() // Group related controls
.defaultFocus($focus, .constant(true)) // Set default

// ✅ CORRECT - Move focus after action
@FocusState private var focusedField: Field?

Button("Next") {
  focusedField = .next
}
swift
// ✅ 正确 - 设置初始焦点
.focusSection() // 分组相关控件
.defaultFocus($focus, .constant(true)) // 设置默认焦点

// ✅ 正确 - 操作后移动焦点
@FocusState private var focusedField: Field?

Button("Next") {
  focusedField = .next
}

Testing (iPadOS/macOS)

测试方法(iPadOS/macOS)

  1. Connect keyboard to iPad or use Mac
  2. Press Tab - does focus move to interactive elements?
  3. Press Space/Return - does focused element activate?
  4. Check custom controls have visible focus indicator
  5. Can you reach all functionality without mouse/touch?

  1. 为iPad连接键盘或使用Mac
  2. 按Tab键 - 焦点是否在交互元素间正确移动?
  3. 按空格/回车键 - 焦点元素是否能激活?
  4. 检查自定义控件是否有可见的焦点指示器
  5. 无需鼠标/触摸能否访问所有功能?

6. Reduce Motion Support (MEDIUM - Vestibular Disorders)

6. Reduce Motion支持(中优先级 - 前庭障碍用户适配)

Problem Animations cause discomfort, nausea, or seizures for users with vestibular disorders.
WCAG 2.3.3 Animation from Interactions (Level AAA - motion animation can be disabled)
问题 动画效果会导致前庭障碍用户不适、恶心或癫痫发作。
WCAG标准 2.3.3 交互动画(AAA级要求 - 可禁用动态动画)

Common violations

常见违规示例

swift
// ❌ WRONG - Always animates (can cause nausea)
.onAppear {
  withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
    scale = 1.0
  }
}

// ❌ WRONG - Parallax scrolling without opt-out
ScrollView {
  GeometryReader { geo in
    Image("hero")
      .offset(y: geo.frame(in: .global).minY * 0.5) // Parallax
  }
}

// ✅ CORRECT - Respect Reduce Motion preference
.onAppear {
  if UIAccessibility.isReduceMotionEnabled {
    scale = 1.0 // Instant
  } else {
    withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
      scale = 1.0
    }
  }
}

// ✅ CORRECT - Simpler animation or cross-fade
if UIAccessibility.isReduceMotionEnabled {
  // Cross-fade or instant change
  withAnimation(.linear(duration: 0.2)) {
    showView = true
  }
} else {
  // Complex spring animation
  withAnimation(.spring()) {
    showView = true
  }
}
swift
// ❌ 错误 - 始终播放动画(可能导致恶心)
.onAppear {
  withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
    scale = 1.0
  }
}

// ❌ 错误 - 视差滚动未提供关闭选项
ScrollView {
  GeometryReader { geo in
    Image("hero")
      .offset(y: geo.frame(in: .global).minY * 0.5) // 视差效果
  }
}

// ✅ 正确 - 遵循Reduce Motion偏好设置
.onAppear {
  if UIAccessibility.isReduceMotionEnabled {
    scale = 1.0 // 直接显示
  } else {
    withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
      scale = 1.0
    }
  }
}

// ✅ 正确 - 使用简化动画或淡入淡出
if UIAccessibility.isReduceMotionEnabled {
  // 淡入淡出或直接切换
  withAnimation(.linear(duration: 0.2)) {
    showView = true
  }
} else {
  // 复杂弹簧动画
  withAnimation(.spring()) {
    showView = true
  }
}

SwiftUI modifier

SwiftUI修饰符

swift
// ✅ CORRECT - Automatic support
.animation(.spring(), value: isExpanded)
.transaction { transaction in
  if UIAccessibility.isReduceMotionEnabled {
    transaction.animation = nil // Disable animation
  }
}
swift
// ✅ 正确 - 自动支持Reduce Motion
.animation(.spring(), value: isExpanded)
.transaction { transaction in
  if UIAccessibility.isReduceMotionEnabled {
    transaction.animation = nil // 禁用动画
  }
}

Testing

测试方法

  1. Settings → Accessibility → Motion → Reduce Motion (toggle ON)
  2. Navigate app - are animations reduced or eliminated?
  3. Test: Transitions, scrolling effects, parallax, particle effects
  4. Video autoplay should also respect this preference

  1. 设置 → 无障碍 → 运动 → 减少动态效果(开启)
  2. 导航应用 - 动画是否被减少或移除?
  3. 测试:转场效果、滚动效果、视差、粒子效果
  4. 视频自动播放也应遵循此偏好设置

7. Common Violations (HIGH - App Store Review)

7. 常见违规(高优先级 - App Store审核)

Images Without Labels

无标签图片

swift
// ❌ WRONG - Informative image without label
Image("product-photo")

// ✅ CORRECT - Informative image with label
Image("product-photo")
  .accessibilityLabel("Red sneakers with white laces")

// ✅ CORRECT - Decorative image hidden
Image("background-pattern")
  .accessibilityHidden(true)
swift
// ❌ 错误 - 信息性图片未设置标签
Image("product-photo")

// ✅ 正确 - 信息性图片添加标签
Image("product-photo")
  .accessibilityLabel("Red sneakers with white laces")

// ✅ 正确 - 隐藏装饰性图片
Image("background-pattern")
  .accessibilityHidden(true)

Buttons With Wrong Traits

错误特性的按钮

swift
// ❌ WRONG - Custom button without button trait
Text("Submit")
  .onTapGesture {
    submit()
  }
// VoiceOver announces as "Submit, text" not "Submit, button"

// ✅ CORRECT - Use Button for button-like controls
Button("Submit") {
  submit()
}
// VoiceOver announces as "Submit, button"

// ✅ CORRECT - Custom control with correct trait
Text("Submit")
  .accessibilityAddTraits(.isButton)
  .onTapGesture {
    submit()
  }
swift
// ❌ 错误 - 自定义按钮未设置按钮特性
Text("Submit")
  .onTapGesture {
    submit()
  }
// VoiceOver会播报为“Submit,文本”而非“Submit,按钮”

// ✅ 正确 - 对按钮类控件使用Button
Button("Submit") {
  submit()
}
// VoiceOver会播报为“Submit,按钮”

// ✅ 正确 - 为自定义控件设置正确特性
Text("Submit")
  .accessibilityAddTraits(.isButton)
  .onTapGesture {
    submit()
  }

Inaccessible Custom Controls

无障碍支持缺失的自定义控件

swift
// ❌ WRONG - Custom slider without accessibility support
struct CustomSlider: View {
  @Binding var value: Double

  var body: some View {
    // Drag gesture only, no VoiceOver support
    GeometryReader { geo in
      // ...
    }
    .gesture(DragGesture()...)
  }
}

// ✅ CORRECT - Custom slider with accessibility actions
struct CustomSlider: View {
  @Binding var value: Double

  var body: some View {
    GeometryReader { geo in
      // ...
    }
    .gesture(DragGesture()...)
    .accessibilityElement()
    .accessibilityLabel("Volume")
    .accessibilityValue("\(Int(value))%")
    .accessibilityAdjustableAction { direction in
      switch direction {
      case .increment:
        value = min(value + 10, 100)
      case .decrement:
        value = max(value - 10, 0)
      @unknown default:
        break
      }
    }
  }
}
swift
// ❌ 错误 - 自定义滑块未支持无障碍
struct CustomSlider: View {
  @Binding var value: Double

  var body: some View {
    // 仅支持拖拽手势,无VoiceOver支持
    GeometryReader { geo in
      // ...
    }
    .gesture(DragGesture()...)
  }
}

// ✅ 正确 - 支持无障碍操作的自定义滑块
struct CustomSlider: View {
  @Binding var value: Double

  var body: some View {
    GeometryReader { geo in
      // ...
    }
    .gesture(DragGesture()...)
    .accessibilityElement()
    .accessibilityLabel("Volume")
    .accessibilityValue("\(Int(value))%")
    .accessibilityAdjustableAction { direction in
      switch direction {
      case .increment:
        value = min(value + 10, 100)
      case .decrement:
        value = max(value - 10, 0)
      @unknown default:
        break
      }
    }
  }
}

Missing State Announcements

缺失状态播报

swift
// ❌ WRONG - State change without announcement
Button("Toggle") {
  isOn.toggle()
}

// ✅ CORRECT - State change with announcement
Button("Toggle") {
  isOn.toggle()
  UIAccessibility.post(
    notification: .announcement,
    argument: isOn ? "Enabled" : "Disabled"
  )
}

// ✅ CORRECT - Automatic state with accessibilityValue
Button("Toggle") {
  isOn.toggle()
}
.accessibilityValue(isOn ? "Enabled" : "Disabled")
swift
// ❌ 错误 - 状态变化未播报
Button("Toggle") {
  isOn.toggle()
}

// ✅ 正确 - 状态变化时播报
Button("Toggle") {
  isOn.toggle()
  UIAccessibility.post(
    notification: .announcement,
    argument: isOn ? "Enabled" : "Disabled"
  )
}

// ✅ 正确 - 通过accessibilityValue自动播报状态
Button("Toggle") {
  isOn.toggle()
}
.accessibilityValue(isOn ? "Enabled" : "Disabled")

Accessibility Inspector Workflow

Accessibility Inspector使用流程

1. Launch Accessibility Inspector

1. 启动Accessibility Inspector

Xcode → Open Developer Tool → Accessibility Inspector
Xcode → 打开开发者工具 → Accessibility Inspector

2. Select Target

2. 选择目标

  • Dropdown: Choose running simulator or connected device
  • Target: Select your app
  • 下拉菜单:选择运行中的模拟器或连接的设备
  • 目标:选择你的应用

3. Inspection Mode

3. 检查模式

  • Click "Inspection Pointer" button (crosshair icon)
  • Hover over UI elements to see:
    • Label, Value, Hint, Traits
    • Frame, Path
    • Actions available
    • Parent/child hierarchy
  • 点击“检查指针”按钮(十字图标)
  • 悬停在UI元素上可查看:
    • 标签、值、提示、特性
    • 框架、路径
    • 可用操作
    • 父子层级

4. Run Audit

4. 运行审核

  • Click "Audit" tab
  • Click "Run Audit" button
  • Review findings:
    • Contrast — Color contrast issues
    • Hit Region — Touch target size issues
    • Clipped Text — Text truncation with Dynamic Type
    • Element Description — Missing labels/hints
    • Traits — Wrong accessibility traits
  • 点击“审核”标签
  • 点击“运行审核”按钮
  • 查看结果:
    • 对比度 — 色彩对比度问题
    • 点击区域 — 触摸目标尺寸问题
    • 文本截断 — Dynamic Type下的文本截断
    • 元素描述 — 缺失标签/提示
    • 特性 — 错误的无障碍特性

5. Fix and Re-Test

5. 修复并重新测试

  • Click each finding for details
  • Fix in code
  • Re-run audit to verify
  • 点击每个结果查看详情
  • 在代码中修复
  • 重新运行审核验证修复效果

VoiceOver Testing Checklist

VoiceOver测试清单

Enable VoiceOver

启用VoiceOver

  • Simulator Cmd+F5 or Settings → Accessibility → VoiceOver
  • Device Triple-click side button (if enabled in Settings)
  • 模拟器 Cmd+F5 或 设置 → 无障碍 → VoiceOver
  • 设备 三击侧边按钮(需在设置中开启)

Navigation Testing

导航测试

  1. ☐ Swipe right/left - moves logically through UI elements
  2. ☐ Each element announces purpose clearly
  3. ☐ No unlabeled elements (except decorative)
  4. ☐ Heading navigation works (swipe up/down with 2 fingers)
  5. ☐ Container navigation works (swipe left/right with 3 fingers)
  1. ☐ 左右滑动 - 按逻辑顺序遍历UI元素
  2. ☐ 每个元素清晰播报用途
  3. ☐ 无未标记元素(装饰性元素除外)
  4. ☐ 标题导航正常(双指上下滑动)
  5. ☐ 容器导航正常(三指左右滑动)

Interaction Testing

交互测试

  1. ☐ Double-tap activates buttons
  2. ☐ Swipe up/down adjusts sliders/pickers (with
    .accessibilityAdjustableAction
    )
  3. ☐ Custom gestures have VoiceOver equivalents
  4. ☐ Text fields announce keyboard type
  5. ☐ State changes are announced
  1. ☐ 双击可激活按钮
  2. ☐ 上下滑动可调节滑块/选择器(需配合
    .accessibilityAdjustableAction
  3. ☐ 自定义手势有VoiceOver替代操作
  4. ☐ 文本框播报键盘类型
  5. ☐ 状态变化会被播报

Content Testing

内容测试

  1. ☐ Images have descriptive labels or are hidden
  2. ☐ Error messages are announced
  3. ☐ Loading states are announced
  4. ☐ Modal sheets announce role
  5. ☐ Alerts announce automatically
  1. ☐ 图片有描述性标签或被隐藏
  2. ☐ 错误信息会被播报
  3. ☐ 加载状态会被播报
  4. ☐ 模态弹窗播报其角色
  5. ☐ 警告会自动播报

App Store Review Preparation

App Store审核准备

Required Accessibility Features (iOS)

iOS应用必填无障碍功能

  1. VoiceOver Support
    • All UI elements must have labels
    • Navigation must be logical
    • All actions must be performable
  2. Dynamic Type
    • Text must scale from -3 to +12 sizes
    • Layout must adapt without clipping
  3. Sufficient Contrast
    • Minimum 4.5:1 for normal text
    • Minimum 3:1 for large text (≥18pt)
  1. VoiceOver支持
    • 所有UI元素必须有标签
    • 导航逻辑合理
    • 所有操作均可通过VoiceOver执行
  2. Dynamic Type支持
    • 文本需支持从-3到+12的尺寸缩放
    • 布局需自适应,无文本截断
  3. 足够的对比度
    • 普通文本最小4.5:1对比度
    • 大文本(≥18pt)最小3:1对比度

App Store Connect Metadata

App Store Connect元数据

When submitting:
  1. Accessibility → Select features your app supports:
    • ☑ VoiceOver
    • ☑ Dynamic Type
    • ☑ Increased Contrast
    • ☑ Reduce Motion (if supported)
  2. Test Notes: Document accessibility testing
    Accessibility Testing Completed:
    - VoiceOver: All screens tested with VoiceOver enabled
    - Dynamic Type: Tested at all size categories
    - Color Contrast: Verified 4.5:1 minimum contrast
    - Touch Targets: All buttons minimum 44x44pt
    - Reduce Motion: Animations respect user preference
提交应用时:
  1. 无障碍 → 选择你的应用支持的功能:
    • ☑ VoiceOver
    • ☑ Dynamic Type
    • ☑ 增强对比度
    • ☑ 减少动态效果(如果支持)
  2. 测试说明:记录无障碍测试情况
    无障碍测试已完成:
    - VoiceOver:所有页面已在VoiceOver开启状态下测试
    - Dynamic Type:已测试所有尺寸类别
    - 色彩对比度:已验证符合最小4.5:1对比度要求
    - 触摸目标:所有按钮均满足44x44pt最小尺寸
    - 减少动态效果:动画遵循用户偏好设置

Common Rejection Reasons

常见拒绝原因

  1. "App is not fully functional with VoiceOver"
    • Missing labels on images/buttons
    • Unlabeled custom controls
    • Actions not performable with VoiceOver
  2. "Text is not readable at all Dynamic Type sizes"
    • Fixed font sizes
    • Text clipping at large sizes
    • Layout breaks at accessibility sizes
  3. "Insufficient color contrast"
    • Text fails 4.5:1 ratio
    • UI elements fail 3:1 ratio
    • Color-only indicators

  1. “应用在VoiceOver下无法完全正常使用”
    • 图片/按钮缺失标签
    • 自定义控件未标记
    • 操作无法通过VoiceOver执行
  2. “文本在所有Dynamic Type尺寸下不可读”
    • 使用固定字体大小
    • 大尺寸下文本被截断
    • 无障碍尺寸下布局崩溃
  3. “色彩对比度不足”
    • 文本未达到4.5:1对比度
    • UI元素未达到3:1对比度
    • 仅用颜色区分状态

Design Review Pressure: Defending Accessibility Requirements

设计评审压力:维护无障碍要求

The Problem

问题场景

Under design review pressure, you'll face requests to:
  • "Those VoiceOver labels make the code messy - can we skip them?"
  • "Dynamic Type breaks our carefully designed layout - let's lock font sizes"
  • "The high contrast requirement ruins our brand aesthetic"
  • "44pt touch targets are too big - make them smaller for a cleaner look"
These sound like reasonable design preferences. But they violate App Store requirements and exclude 15% of users. Your job: defend using App Store guidelines and legal requirements, not opinion.
在设计评审中,你可能会遇到以下要求:
  • “这些VoiceOver标签让代码变乱了——能不能跳过?”
  • “Dynamic Type破坏了我们精心设计的布局——锁定字体大小吧”
  • “高对比度要求毁了我们的品牌美学”
  • “44pt的触摸目标太大了——做小一点让界面更简洁”
这些听起来是合理的设计偏好,但它们违反了App Store要求,并且将15%的用户排除在外。你的职责:用App Store指南和法律要求而非个人观点来维护无障碍标准。

Red Flags — Designer Requests That Violate Accessibility

违反无障碍要求的设计师请求警示

If you hear ANY of these, STOP and reference this skill:
  • "Skip VoiceOver labels on icon-only buttons" – App Store rejection (Guideline 2.5.1)
  • "Use fixed 14pt font for compact design" – Excludes users with vision disabilities
  • "3:1 contrast ratio is fine" – Fails WCAG AA for text (needs 4.5:1)
  • "Make buttons 36x36pt for clean aesthetic" – Fails touch target requirement (44x44pt minimum)
  • "Disable Dynamic Type in this screen" – App Store rejection risk
  • "Color-code without labels (red=error, green=success)" – Excludes colorblind users (8% of men)
如果你听到以下任何说法,立即停止并参考本技能
  • “纯图标按钮跳过VoiceOver标签” – 会导致App Store审核拒绝(指南2.5.1)
  • “为了紧凑设计使用固定14pt字体” – 排除视障用户
  • “3:1的对比度就够了” – 未达到WCAG AA级文本要求(需要4.5:1)
  • “把按钮做成36x36pt让界面更简洁” – 未满足触摸目标最小44pt的要求
  • “在这个页面禁用Dynamic Type” – 有被App Store拒绝的风险
  • “仅用颜色标记状态(红色=错误,绿色=成功)” – 排除色盲用户(男性中占8%)

How to Push Back Professionally

专业地反驳的方法

Step 1: Show the Guideline

步骤1:展示官方指南

"I want to support this design direction, but let me show you Apple's App Store
Review Guideline 2.5.1:

'Apps should support accessibility features such as VoiceOver and Dynamic Type.
Failure to include sufficient accessibility features may result in rejection.'

Here's what we need for approval:
1. VoiceOver labels on all interactive elements
2. Dynamic Type support (can't lock font sizes)
3. 4.5:1 contrast ratio for text, 3:1 for UI
4. 44x44pt minimum touch targets

Let me show where our design currently falls short..."
“我想支持这个设计方向,但请允许我展示苹果的App Store审核指南2.5.1:

'应用应支持VoiceOver和Dynamic Type等无障碍功能。未提供足够无障碍功能可能导致审核拒绝。'

我们需要满足以下要求才能通过审核:
1. 所有交互元素添加VoiceOver标签
2. 支持Dynamic Type(不能锁定字体大小)
3. 文本对比度4.5:1,UI元素对比度3:1
4. 触摸目标最小44x44pt

让我展示当前设计不符合要求的地方……”

Step 2: Demonstrate the Risk

步骤2:演示风险

Open the app with accessibility features enabled:
  • VoiceOver (Cmd+F5): Show buttons announcing "Button" instead of purpose
  • Largest Text Size: Show layout breaking or text clipping
  • Color Contrast Analyzer: Show failing contrast ratios
  • Touch target overlay: Show targets < 44pt
开启无障碍功能展示应用:
  • VoiceOver(Cmd+F5):展示按钮播报“按钮”而非具体用途
  • 最大文本尺寸:展示布局崩溃或文本截断
  • 色彩对比度分析工具:展示不达标对比度
  • 触摸目标叠加层:展示小于44pt的目标

Reference

参考依据

  • App Store Review Guideline 2.5.1
  • WCAG 2.1 Level AA (industry standard)
  • ADA compliance requirements (legal risk in US)
  • App Store审核指南2.5.1
  • WCAG 2.1 AA级(行业标准)
  • ADA合规要求(美国的法律风险)

Step 3: Offer Compromise

步骤3:提出折中方案

"I can achieve your aesthetic goals while meeting accessibility requirements:

1. VoiceOver labels: Add them programmatically (invisible in UI, required for approval)
2. Dynamic Type: Use layout techniques that adapt (examples from Apple HIG)
3. Contrast: Adjust colors slightly to meet 4.5:1 (I'll show options that preserve brand)
4. Touch targets: Expand hit areas programmatically (visual size stays the same)

These changes won't affect the visual design you're seeing, but they're required
for App Store approval and legal compliance."
“我可以在满足无障碍要求的同时实现你的美学目标:

1. VoiceOver标签:通过代码添加(界面不可见,但为审核必填)
2. Dynamic Type:使用自适应布局技巧(参考Apple人机交互指南示例)
3. 对比度:微调颜色以达到4.5:1(我会提供保留品牌风格的选项)
4. 触摸目标:通过代码扩大点击区域(视觉尺寸保持不变)

这些修改不会影响你看到的视觉设计,但它们是App Store审核通过和合规的必要条件。”

Step 4: Document the Decision

步骤4:记录决策

If overruled (designer insists on violations):
Slack message to PM + designer:

"Design review decided to proceed with:
- Fixed font sizes (disabling Dynamic Type)
- 38x38pt buttons (below 44pt requirement)
- 3.8:1 text contrast (below 4.5:1 requirement)

Important: These changes violate App Store Review Guideline 2.5.1 and WCAG AA.
This creates three risks:

1. App Store rejection during review (adds 1-2 week delay)
2. ADA compliance issues if user files complaint (legal risk)
3. 15% of potential users unable to use app effectively

I'm flagging this proactively so we can prepare a response plan if rejected."
如果设计师坚持违规(即使你反对):
发送给产品经理和设计师的Slack消息:

“设计评审决定采用以下方案:
- 使用固定字体大小(禁用Dynamic Type)
- 按钮尺寸38x38pt(低于44pt要求)
- 文本对比度3.8:1(低于4.5:1要求)

重要提示:这些修改违反了App Store审核指南2.5.1和WCAG AA级标准,带来三个风险:

1. App Store审核拒绝(会导致1-2周的延迟)
2. 如果用户投诉,可能面临ADA合规的法律风险(美国)
3. 15%的潜在用户无法有效使用应用

我提前标记此问题,以便我们在被拒绝时能准备好应对方案。”

Why this works

为什么这有效

  • You're not questioning their design taste
  • You're raising App Store rejection risk (business impact)
  • You're citing specific guidelines (not opinion)
  • You're offering solutions that preserve visual design
  • You're documenting the decision (protects you post-rejection)
  • 你没有质疑他们的设计品味
  • 你提出了App Store拒绝的业务影响
  • 你引用了具体的指南(而非个人观点)
  • 你提供了保留视觉设计的解决方案
  • 你记录了决策(在被拒绝后保护自己)

Real-World Example: App Store Rejection (48-Hour Resubmit Window)

真实案例:App Store拒绝后的48小时重新提交窗口

Scenario

场景

  • 48 hours until resubmit deadline after rejection
  • Apple cited: "2.5.1 - Insufficient VoiceOver support"
  • Designer says: "Just add generic labels quickly"
  • PM watching the meeting, wants fastest fix
  • 拒绝后有48小时的重新提交期限
  • 苹果拒绝理由:“2.5.1 - VoiceOver支持不足”
  • 设计师说:“快速添加通用标签就行”
  • 产品经理在旁观看,想要最快的修复方案

What to do

正确做法

swift
// ❌ WRONG - Generic labels (will fail re-review)
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Button") // Apple will reject again

// ✅ CORRECT - Descriptive labels (passes review)
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Add to cart")
.accessibilityHint("Double-tap to add this item to your shopping cart")
swift
// ❌ 错误 - 通用标签(会再次被拒绝)
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Button") // 苹果会再次拒绝

// ✅ 正确 - 描述性标签(通过审核)
Button(action: addToCart) {
    Image(systemName: "cart.badge.plus")
}
.accessibilityLabel("Add to cart")
.accessibilityHint("Double-tap to add this item to your shopping cart")

In the meeting, demonstrate

会议中演示

  1. Enable VoiceOver (Cmd+F5)
  2. Show "Button" announcement (generic - fails)
  3. Show "Add to cart" announcement (descriptive - passes)
  4. Reference Apple's rejection message: "Elements must have descriptive labels"
Time estimate 2-4 hours to audit all interactive elements and add proper labels.
  1. 启用VoiceOver(Cmd+F5)
  2. 展示“按钮”的播报(通用标签 - 不通过)
  3. 展示“Add to cart”的播报(描述性标签 - 通过)
  4. 引用苹果的拒绝消息:“元素必须有描述性标签”
时间预估 2-4小时审核所有交互元素并添加合适的标签。

Result

结果

  • Honest time estimate prevents second rejection
  • Proper labels pass Apple review
  • Resubmit accepted within 48 hours
  • 诚实的时间预估避免了第二次拒绝
  • 合适的标签通过了苹果审核
  • 48小时内重新提交并通过

When to Accept the Design Decision (Even If You Disagree)

何时接受设计决策(即使你不同意)

Sometimes designers have valid reasons to override accessibility guidelines. Accept if:
  • They understand the App Store rejection risk
  • They're willing to delay launch if rejected
  • You document the decision in writing
  • They commit to fixing if rejected
有时设计师有充分的理由推翻无障碍指南。如果满足以下条件,可以接受:
  • 他们了解App Store拒绝的风险
  • 他们愿意在被拒绝时延迟发布
  • 你以书面形式记录了决策
  • 他们承诺如果被拒绝会修复问题

Document in Slack

在Slack中记录

"Design review decided to proceed with [specific violations].

We understand this creates:
- App Store rejection risk (Guideline 2.5.1)
- Potential 1-2 week delay if rejected
- Need to audit and fix all instances if rejected

Monitoring plan:
- Submit for review with current design
- If rejected, implement proper accessibility (estimated 2-4 hours)
- Have accessibility-compliant version ready as backup"
This protects both of you and shows you're not blocking - just de-risking.

“设计评审决定采用[具体违规方案]。

我们清楚这会带来:
- App Store审核拒绝风险(指南2.5.1)
- 如果被拒绝,可能导致1-2周的延迟
- 如果被拒绝,需要审核并修复所有问题

监控计划:
- 按当前设计提交审核
- 如果被拒绝,实施符合无障碍要求的修复(预估2-4小时)
- 准备好符合无障碍标准的备用版本
这能保护你和设计师,表明你不是在阻碍工作,而是在降低风险。

WCAG Compliance Levels

WCAG合规等级

Level A (Minimum — Required for App Store)

A级(最低要求 — App Store必填)

  • 1.1.1 Non-text Content — Images have text alternatives
  • 2.1.1 Keyboard — All functionality via keyboard (iPadOS/macOS)
  • 4.1.2 Name, Role, Value — Elements have accessible names
  • 1.1.1 非文本内容 — 图片有文本替代
  • 2.1.1 键盘操作 — 所有功能可通过键盘实现(iPadOS/macOS)
  • 4.1.2 名称、角色、值 — 元素有可访问的名称

Level AA (Standard — Recommended)

AA级(标准要求 — 推荐)

  • 1.4.3 Contrast (Minimum) — 4.5:1 text, 3:1 UI
  • 1.4.4 Resize Text — Support 200% text scaling
  • 1.4.5 Images of Text — Use real text when possible
  • 1.4.3 最低对比度 — 文本4.5:1,UI元素3:1
  • 1.4.4 文本缩放 — 支持200%文本缩放
  • 1.4.5 文本图片 — 尽可能使用真实文本

Level AAA (Enhanced — Best Practice)

AAA级(增强要求 — 最佳实践)

  • 1.4.6 Contrast (Enhanced) — 7:1 text, 4.5:1 UI
  • 2.3.3 Animation from Interactions — Reduce Motion support
  • 2.5.5 Target Size - 44x44pt minimum targets
Goal Meet Level AA for all content, Level AAA where feasible.
  • 1.4.6 增强对比度 — 文本7:1,UI元素4.5:1
  • 2.3.3 交互动画 — 支持减少动态效果
  • 2.5.5 目标尺寸 - 最小44x44pt的触摸目标
目标 所有内容达到AA级,尽可能实现AAA级。

Quick Command Reference

快速命令参考

After making fixes:
bash
undefined
修复完成后:
bash
undefined

Quick scan for new issues

快速扫描新问题

/axiom:audit-accessibility
/axiom:audit-accessibility

Deep diagnosis for specific issues

针对特定问题进行深度诊断

/skill axiom:accessibility-diag
undefined
/skill axiom:accessibility-diag
undefined

Resources

资源

Docs: /accessibility/voiceover, /uikit/uifont/scaling_fonts_automatically

Remember Accessibility is not a feature, it's a requirement. 15% of users have some form of disability. Making your app accessible isn't just the right thing to do - it expands your user base and improves the experience for everyone.
文档:/accessibility/voiceover, /uikit/uifont/scaling_fonts_automatically

记住 无障碍不是一个功能,而是一项要求。15%的用户有不同形式的障碍。让你的应用支持无障碍不仅是正确的事,还能扩大用户群体,提升所有人的使用体验。