Accessibility Traitは、VoiceOverに対する要素の役割と動作を決定するiOS要素のプロパティです。トレイトはスクリーンリーダーに、要素がどのように読み上げられるべきか、どのジェスチャーが利用可能か(ボタン、ヘッダー、リンク、検索フィールドなど)を伝えます。Apple UIAccessibilityTraits、2024によると、システムは15以上の定数をサポートしており、ビットマスクを使用して組み合わせることができます。正しく選択されたトレイトは、VoiceOverユーザーのナビゲーション時間を最大50%節約します。
重要なポイント
Accessibility Traitは、VoiceOverに意味的な役割を示すためにUIView要素に設定されるフラグです。トレイトはAppleのアクセシビリティ三要素(Label(名前)、Hint(説明)、Trait(役割))の1つです。iOSはUIAccessibilityTraitsビットマスク(UInt64)を使用し、各ビットは特定の役割に対応します。VoiceOverはLabelとHintの後に役割を読み上げます。「送信ボタン。フォームを開きます」— UIAccessibilityTraitButtonトレイトによって「ボタン」が追加されています。
デフォルトでは、UIButtonはUIAccessibilityTraitButtonを取得し、UILabelはUIAccessibilityTraitStaticTextを、UIImageViewはUIAccessibilityTraitImageを取得します。カスタムコントロールを使用する場合、開発者は手動でトレイトを設定する必要があります。Apple Human Interface Guidelines、2024は、これを「アクセシビリティを確保する上で最も重要なステップの1つ」と呼んでいます。
正しいトレイトがないと、ユーザーはどのジェスチャーを適用すべきかわかりません。シングルタップ(ボタン起動)、ダブルタップ(ズーム)、スワイプジェスチャー(トグル)などです。トレイトは、要素上でどのVoiceOverジェスチャーが有効になるかを決定します。
UIAccessibilityTraitsはtypealias UInt64です。各トレイトは、正確に1ビットが設定された定数です。例えば、UIAccessibilityTraitButton = 0x0000000000000001、UIAccessibilityTraitLink = 0x0000000000000002、UIAccessibilityTraitHeader = 0x0000000000000008です。組み合わせはビットごとのORで実現されます:0x0001 | 0x0008 = 0x0009。VoiceOverはマスクを分析して動作を決定します。
iOSは15以上のトレイト定数を提供しています。90%のシナリオで使用される主要なものを見てみましょう:
| トレイト | 定数 | VoiceOverの動作 |
|---|---|---|
| Button | UIAccessibilityTraitButton | ダブルタップで起動 |
| Header | UIAccessibilityTraitHeader | ヘッダーによるクイックナビゲーション |
| Link | UIAccessibilityTraitLink | リンクとして起動 |
| StaticText | UIAccessibilityTraitStaticText | 読み取り専用、起動なし |
| SearchField | UIAccessibilityTraitSearchField | 特別な動作の検索フィールド |
| Image | UIAccessibilityTraitImage | 画像、起動ジェスチャーなし |
| Selected | UIAccessibilityTraitSelected | 「選択」状態 |
| PlaysSound | UIAccessibilityTraitPlaysSound | 起動時に音を再生 |
| KeyboardKey | UIAccessibilityTraitKeyboardKey | キーボードキー |
| TabBar | UIAccessibilityTraitTabBar | タブバー要素 |
定数はiOS 3.0以降UIKitで利用可能です。iOS 14+では、.accessibilityAddTraits()モディファイアを使用してSwiftUIでUIAccessibilityTraitsがサポートされました。
UIAccessibilityTraitAdjustable — 調整可能な値用(スライダー、ピッカー、音量スライダー)。VoiceOverはaccessibilityIncrementとaccessibilityDecrementで定義されたステップで値を変更するために上下にスワイプできます。UIAccessibilityTraitUpdatesFrequently — 頻繁に変化する値の要素用(タイマー、進行状況インジケーター)。VoiceOverは変更ごとに値を読み上げず、一時停止します。UIAccessibilityTraitAllowsDirectInteraction — ユーザーがVoiceOverジェスチャーを介さずに直接操作できる要素用(キーボード、描画)。
1つの要素に複数のトレイトを同時に持たせることができます — 組み合わせはビットごとのOR(|)で設定します。例:現在選択されているボタン — Button | Selected。VoiceOverは「選択済み。価格でフィルタリング。ボタン」と読み上げます。
コードでのトレイト設定:
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)
// またはマスク経由:
filterButton.accessibilityTraits = [.button, .selected]
トレイトがデフォルトで設定されていないカスタムUIViewの場合:
class CustomToggle: UIControl {
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isOn {
return [.button, .selected]
} else {
return .button
}
}
set {}
}
}
組み合わせルール:要素あたり3〜4個以下のトレイト。過剰なトレイト(例:Button + Link + Header)はVoiceOverの読み上げが長くなり混乱を招きます。Appleによると、「追加のプロパティごとにユーザーの認知負荷が増加します」。
SwiftUIでは、トレイトは.accessibilityAddTraits()と.accessibilityRemoveTraits()モディファイアを使用して設定します。例:Text(「タイトル」).font(.largeTitle).accessibilityAddTraits(.isHeader)。.isHeaderモディファイアはUIAccessibilityTraitHeaderを追加します。SwiftUIのトレイト一覧:.isButton、.isHeader、.isLink、.isSelected、.isImage、.isSearchField、.isKeyboardKey、.isStaticText、.isSummaryElement、.isToggle、.playsSound、.startsMediaSession、.updatesFrequently、.allowsDirectInteraction、.causesPageTurn、.isModal、.tabBar。
Buttonの代わりにStaticText — 視覚的にボタンに見えるカスタムコントロールがデフォルトでStaticTextトレイトを取得します。VoiceOverは起動ジェスチャーを提供しないため、ユーザーは要素を「押す」ことができません。解決策:明示的に.buttonを設定します。
トレイトのない画像 — アクセシビリティが有効なUIImageViewは、実際には写真を拡大するボタンであってもImageトレイトを取得します。.buttonとLabel「写真を拡大」を割り当ててください。WWDC 2023、「Deliver an Exceptional Accessibility Experience」によると、新しいアプリバージョンでのアクセシビリティ回帰の40%は、トレイトの不一致が原因です。
すべての要素にHeader — Headerトレイトは画面の構造的なヘッダーを対象としています。すべてのUILabelをヘッダーにすると、「ヘッダー」モードのVoiceOverローターが役に立たなくなります — すべての単語で停止します。
トレイトを失う一般的な原因はリファクタリングです。開発者がカスタム表示のためにUIButtonをUIControlに置き換えると、UIButtonは自動的にButtonトレイトを取得しますが、UIControlは取得しません。リファクタリング後は、明示的にaccessibilityTraits = .buttonを設定する必要があります。コードレビューにチェックを追加してください:「UIButtonをUIControlに置き換えた場合 — トレイトを確認してください」。
状態が変化する要素(いいねボタンなど)では、トレイトを動的に変更する必要があります。「いいねされていない」状態ではButton、「いいねされている」状態ではButton + Selected + Image(アイコンがある場合)。VoiceOverの読み上げが変わります:「いいね。ボタン」vs「選択済み。いいね。ボタン」。Selectedトレイトが不十分な場合、状態を伝えるためにaccessibilityValueを使用します。購読ボタン、お気に入り、フィルター、トグルに適用されます。
Androidには、トレイトに直接相当するものはありません。ビットマスクの代わりに、以下が使用されます:
AndroidのカスタムViewでは、onInitializeAccessibilityNodeInfoをオーバーライドする必要があります:
class CustomButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
override fun onInitializeAccessibilityNodeInfo(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(info)
info.className = "android.widget.Button"
info.isClickable = true
}
}
Flutter開発者は、SemanticsウィジェットでsemanticsRoleパラメータを使用する必要があります:button、header、image、link、textFieldなど。さらに、semanticsLabelとsemanticsHintが利用可能です — iOSの三要素Label + Hint + Traitの完全な相当です。
モバイルアプリのWebバージョン(PWA、WebView)では、WAI-ARIAのrole属性が使用されます:role="button"、role="heading"、role="link"。これはaccessibilityTraitsの直接的な相当です。ハイブリッドアプリでは、WebViewがARIAロールをネイティブのアクセシビリティレイヤーに渡していることを確認してください。これには、iOSではUIAccessibilityContainerDataTableプロトコル、AndroidではsetAccessibilityDelegateを使用します。JavaScriptが有効なWebViewはARIAロールを正しく渡さない可能性があるため、個別にテストしてください。
Androidでは、AccessibilityNodeInfoにカスタムアクションを追加できます:AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICKとACTION_LONG_CLICK。これは追加のジェスチャーを持つButtonトレイトに相当します。スライダーにはACTION_SET_PROGRESSを使用します — Adjustableに相当します。SpinnerとDatePickerには — ACTION_SET_SELECTION、ACTION_SET_DATE、ACTION_SET_TIMEを使用します。
Xcode Accessibility InspectorはiOSの主要ツールです:要素を選択してTraitsフィールドを表示すると、設定されたトレイトのリストが表示されます。「要素」モードのVoiceOverローターを使用すると、画面のすべてのコントロールをナビゲートできます。
トレイトを確認する自動化Swiftテスト:
func testSubmitButtonTrait() {
let app = XCUIApplication()
app.launch()
let submitButton = app.buttons["送信"]
XCTAssertTrue(submitButton.isEnabled)
// XCUIElementはトレイトへの直接アクセスを提供しません
// ジェスチャー起動による確認
submitButton.tap()
XCTAssertTrue(app.staticTexts["フォームが送信されました"].exists)
}
VoiceOverによる手動確認:VoiceOverをオンにし、要素までスワイプしてダブルタップします — Buttonであれば要素が起動します。要素がダブルタップに反応しない場合、トレイトが間違っています。Rotorジェスチャーを使用してモードを切り替えます(「ヘッダー」、「リンク」、「ボタン」) — 各モードは対応するトレイトを持つ要素のみを表示します。
iOS 14より前は、ユニットテストはaccessibilityTraitsに直接アクセスできませんでした。iOS 14以降、プロパティが利用可能になりました:XCTAssertEqual(customButton.accessibilityTraits, .button)。カスタムコントロールを検証するためにユニットテストでこれを使用してください。特にリファクタリングや親クラスの変更後は、新しいカスタムUIViewごとにトレイトの正確性をテストすることをお勧めします。
よくある質問
要素あたり3〜4個まで。それ以上多いとVoiceOverの読み上げが冗長になります。Button + Selected、Header + StaticTextの組み合わせを使用してください。
UIAccessibilityTraitButtonです。iOSはすべてのUIButtonインスタンスに自動的に設定します。UIViewから継承してボタンを模倣する場合、トレイトを手動で設定する必要があります。
はい、UIAccessibilityTraitAdjustable — 調整可能な値を持つ要素用です(スライダー、ピッカー、カウンター)。VoiceOverは上下にスワイプして値を変更し、現在の状態を読み上げることができます。
.accessibilityAddTraits()モディファイアを使用します:Text(「タイトル」).font(.title).accessibilityAddTraits(.isHeader)。このメソッドはiOS 14+で動作します。
VoiceOverはNoneトレイトを割り当てます。要素には役割がなくなり、スクリーンリーダーはタイプを示さずにLabelのみを読み上げます。ユーザーは起動ジェスチャーが利用可能かどうかわかりません。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。