Focus Order — その概要、原則、モバイルアプリでの設定方法

著者: IT Sectr 公開日: 2026-05-16 読了時間: 9 分

Focus Orderとは、キーボード、Switch Control、VoiceOver、TalkBackを使用してナビゲートする際に、インターフェース要素がフォーカスを受け取る順序です。モバイルアプリケーションでは、フォーカス順序によって、ユーザーがジェスチャーやボタンを使用してコントロール間を移動する方法が決まります。W3C WCAG 2.2、Success Criterion 2.4.3、2023によると、フォーカスはコンテンツの意味を保持する論理的な順序に従う必要があります。この原則の違反は、アクセシビリティ監査が不合格になる一般的な原因の一つです。

重要なポイント

  • Focus Order — キーボードやスクリーンリーダーでナビゲートする際のインタラクティブ要素の移動順序
  • フォーカスは視覚的な順序(左から右、上から下)に従い、コンテンツの論理性を保持する必要があります
  • iOSでは、shouldGroupAccessibilityElementとaccessibilityElements配列によって順序が制御されます
  • Androidでは、nextFocusDown、nextFocusUp、nextFocusLeft、nextFocusRight属性がフォーカスの隣接要素を定義します
  • カスタム画面(マップ、キャンバス、ゲーム)では、UIAccessibilityPostNotificationによるプログラムによるフォーカス管理が必要です

アクセシビリティにおけるFocus Orderとは

Focus Orderとは、ユーザーが代替入力方法(キーボード(Tab)、Switch Control(ステップバイステップ)、VoiceOver(右/左スワイプ)、TalkBack)を使用してインタラクティブ要素間を移動する順序です。マウスやタッチスクリーンとは異なり、ユーザーが直接要素を選択するのではなく、フォーカスナビゲーションは線形的で、各ステップで次の要素にフォーカスが移動します。

Apple HIG、2024によると、VoiceOverはアクセシビリティツリー内の要素の順序を使用します。これは視覚的な配置(左上隅→右下隅)に基づいて構築されます。画面に複雑なレイアウト(列、Grid、ZStack)がある場合、ツリーが視覚的な順序と一致しない可能性があります。

WCAG 2.4.3の原則:“ウェブページをセクションごとに順次ナビゲートでき、フォーカス順序が意味に影響を与える場合、フォーカスは意味と操作性を保持する順序に従わなければならない”。例外:注意を引くためにフォーカスがジャンプする可能性のある動的コンテンツ(アラート、モーダルウィンドウ)。

Focus Orderがアクセシビリティにとって重要な理由

Switch Controlユーザー(運動障害を持つ人々)は、自動的に要素間を移動します(サイクルを繰り返します)。順序が壊れていると、ユーザーはフォームの完了に3倍の時間がかかります。Deque University、2024によると、正しいFocus Orderは、支援技術ユーザーのフォーム完了時間を60%削減します。

Focus Orderとモーダルウィンドウ

特別な注意が必要なのはモーダルウィンドウです。モーダルを開いた後、フォーカスはモーダル内の最初のインタラクティブ要素(通常は“閉じる”または“確認”ボタン)に直ちに移動する必要があります。閉じた後は、モーダルをトリガーした要素に戻る必要があります。これはWCAG 2.4.3の要件であり、同時によくある間違いでもあります。

iOS:フォーカス順序の管理

iOSでは、VoiceOverがジオメトリに基づいて自動的に順序を構築します。要素はYでソートされ、次にXでソートされます。複雑な構造の画面では、この順序が正しくない場合があり、開発者が介入する必要があります。

主なツール:

  • shouldGroupAccessibilityElement — 子要素を1つの論理ブロックにグループ化します
  • accessibilityElements — 子要素のカスタム順序を定義する配列
  • UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element) — プログラムによるフォーカスの移動

商品カードのカスタム順序設定の例:

swift
class ProductCardView: UIView {
    let titleLabel = UILabel()
    let priceLabel = UILabel()
    let buyButton = UIButton()

    override var accessibilityElements: [Any]? {
        get {
            return [titleLabel!, priceLabel!, buyButton!]
        }
        set {}
    }
}

アクション後のプログラムによるフォーカス移動:

swift
UIAccessibility.post(
    notification: .layoutChanged,
    argument: newlyAddedItem
)

実際のshouldGroupAccessibilityElement

shouldGroupAccessibilityElementプロパティは、コレクション内のカードに便利です。親カードでtrueに設定すると、VoiceOverはカード全体を1つの要素として認識します。ユーザーはダブルタップでカード全体をアクティブにするか、内部ナビゲーション用にローターを設定できます。UICollectionViewCellおよびUITableViewCellに推奨されます。

Android:フォーカス方向属性

Androidでは、TalkBackも幾何学的な順序を使用しますが、明示的なnextFocus*属性が優先されます。これらの属性はXMLまたはプログラムで設定されます:

属性目的
nextFocusDown下にナビゲートするときの要素@+id/field_email
nextFocusUp上にナビゲートするときの要素@+id/field_name
nextFocusLeft左の要素@+id/btn_back
nextFocusRight右の要素@+id/btn_next

登録フォームの例:

xml
<EditText
    android:id="@+id/field_email"
    android:nextFocusDown="@+id/field_password" />

<EditText
    android:id="@+id/field_password"
    android:nextFocusDown="@+id/btn_submit" />

RecyclerViewの場合、フォーカス順序は動的で、アダプターによって決定されます。セルの構造が複雑な場合は、descendantFocusability = “beforeDescendants”を設定し、リストアイテムノードで順序を定義します。Jetpack Composeの場合、フォーカス順序はModifier.focusOrder()とFocusOrderで設定します。優先順位:previous(子)、next(次)、カスタムキー。

TouchDelegate、ヒット領域、フォーカス領域

要素がフォーカスに小さすぎる場合(44pt未満)、iOSのTouchDelegateまたはAndroidのminWidth/minHeightを使用してヒット領域を拡大します。Google Material Design、2024によると、最小タッチ領域は48×48dpです。VoiceOverとTalkBackは要素のバウンディングボックスにフォーカスします。30pt未満の要素はジェスチャーフォーカスにアクセスできない可能性があります。ユーザーは物理的にそれらをタップできません。

WCAG 2.4.3の一般的な違反

ジャンプするフォーカス — アクション(要素の削除など)の後、フォーカスがリストの先頭またはシステムの“戻る”ボタンに移動します。VoiceOverユーザーはコンテキストを失います。解決策:削除された要素に最も近い要素にプログラムでフォーカスを移動します。

非表示のフォーカス — 要素がフォーカスを受け取っても視覚的なインジケーターがありません(キーボードユーザーは現在位置がわかりません)。iOSでは、カスタムインジケーターのためにUIAccessibility.isVoiceOverRunningを確認します。Deque University、2024によると、非表示のフォーカスはアクセシビリティ監査不合格の2番目に多い原因です。

モーダル — モーダルを開いた後もフォーカスが背景コンテンツに残ります。iOSでは、modalPresentationStyle = .pageSheetが設定されている場合、モーダルビューが自動的にフォーカスを取得します。Androidでは、ダイアログコンテナにsetFocusable(true)を使用します。

フォーカストラップ

逆の問題:フォーカスがモーダル内に閉じ込められ、閉じる以外に抜け出せません。これはモーダルウィンドウのみ許容されます。ユーザーは意図的にウィンドウを閉じる必要があります。通常の画面では、フォーカストラップは重大なエラーです。解決策:モーダルの最後の要素(“閉じる”ボタン)がフォーカスを戻すことを確認します。

カスタム画面とプログラムによるフォーカス

カスタム画面(マップ、キャンバス、ゲーム)では、自動的な幾何学的順序は適用できません。開発者はアクセシビリティツリーを手動で構築する必要があります。iOSでは、このためにUIAccessibilityContainerメソッドがオーバーライドされます。

カスタムキャンバスの例:

swift
class CanvasView: UIView {
    var shapes: [ShapeView] = []

    override var accessibilityElements: [Any]? {
        get {
            // Zインデックスで図形を並べ替え、ジオメトリではない
            return shapes.sorted { $0.zIndex < $1.zIndex }
        }
        set {}
    }
}

Androidでは、カスタムViewに対してonInitializeAccessibilityNodeInfoをオーバーライドします:

kotlin
override fun onInitializeAccessibilityNodeInfo(
    info: AccessibilityNodeInfo
) {
    super.onInitializeAccessibilityNodeInfo(info)
    info.addChild(firstElement)
    info.addChild(secondElement)
    info.isFocusable = true
}

動的リスト(チャット、ニュースフィード)の場合、要素を追加した後、最初の新しい要素にフォーカスを移動します。iOS:UIAccessibility.post(notification: .layoutChanged, argument: newMessage)。Android:sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)。

AccessibilityFrameとフォーカスのジオメトリ

iOSは要素のフレームに基づいて自動的にフォーカス領域を決定します。要素に変換(transform、rotation)がある場合、VoiceOverが間違った領域にフォーカスする可能性があります。画面座標でaccessibilityFrameを明示的に設定します:element.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(element.bounds, in: element)。これにより、VoiceOverが正しい領域を強調表示することが保証されます。

UIKit Dynamicsとアクセシビリティ

アニメーション画面(UIKit Dynamics、Lottie、SpriteKit)の場合、プログラムによるフォーカスが特に重要です。VoiceOverは動的に移動する要素のアクセシビリティツリーを構築できません。アニメーションコンテナではisAccessibilityElement = falseを設定し、内部のインタラクティブ要素のみtrueを設定します。

フォーカス順序のテスト

手動テスト:VoiceOver(iOS)またはTalkBack(Android)を有効にし、シーケンス全体を右にスワイプします。フォーカスは視覚的な順序(左から右、上から下)に従う必要があります。各インタラクティブ要素は正確に1回フォーカスを受け取る必要があります。

自動テストは困難ですが可能です:

swift
func testKeyboardFocusOrder() {
    let app = XCUIApplication()
    app.launch()
    app.textFields["Email"].tap()
    // Tab — ハードウェアキーボードのみ
}

Androidの場合は、Accessibility Testing Frameworkを使用します:

kotlin
@Test
fun testFocusOrder() {
    onView(withId(R.id.fieldEmail))
        .check(matches(isFocusable()))
    onView(withId(R.id.fieldEmail))
        .perform(focus())
    onView(withId(R.id.fieldPassword))
        .check(matches(isFocused()))
}

最も信頼性の高い方法はUIシナリオテストです:フォームを段階的に入力し(メール→パスワード→送信)、各ステップが正常に完了することを確認します。フォーカス順序が壊れている場合、フォーカス外の要素と対話しようとするとシナリオは失敗します。

デバッグのためのXcode Accessibility Inspector

XcodeのAccessibility Inspectorツールは、完全なアクセシビリティツリーを表示します。VoiceOverの順序で要素を移動し、正確なフォーカスパスを確認できます。“Audit”タブを使用して、Focus Order違反を自動的に検出します。

よくある質問

WCAG 2.4.3とは何ですか?フォーカスの要件は?

WCAG 2.4.3(Focus Order)はレベルAの成功基準です。順次ナビゲーション中にフォーカス順序がコンテンツの意味を保持することを要求します。違反は重大と見なされ、認証をブロックします。

アニメーションの背後に隠れた要素のフォーカス順序を設定するには?

非表示の要素は、iOSではisAccessibilityElement = false、Androidではvisibility = gone/invisibleにする必要があります。表示されたら、UIAccessibility.post(notification: .layoutChanged)を使用してプログラムでフォーカスを移動します。

iOSとAndroidでのフォーカスの違いは?

iOSはaccessibilityElementsとshouldGroupAccessibilityElementで管理し、AndroidはnextFocus*属性とAccessibilityNodeInfoで管理します。原則は同じです:デフォルトでは幾何学的順序で、上書きが可能です。

RecyclerViewの順序が間違っている場合の対処法は?

ルート要素にdescendantFocusability = “beforeDescendants”を設定し、各セルのonInitializeAccessibilityNodeInfoを介してアダプターで順序を設定します。

VoiceOverなしでフォーカスをテストするには?

BluetoothまたはUSB経由でハードウェアキーボードを接続します。iOSでTabを押してフォーカスを移動します。AndroidでTalkBackを有効にし、Tabキーと矢印キーを使用します。

まとめ

  • Focus Order — キーボードやスクリーンリーダーでナビゲートする際の要素移動順序、WCAG 2.4.3に基づく
  • フォーカスは視覚的な順序(左から右、上から下)に従う — VoiceOverとTalkBackで自動的に
  • iOSでは、順序はaccessibilityElementsとshouldGroupAccessibilityElementで制御
  • AndroidではnextFocusDown、nextFocusUp、nextFocusLeft、nextFocusRight属性を使用
  • カスタム画面(マップ、キャンバス)ではUIAccessibilityPostNotificationによるプログラムによるフォーカス管理が必要
  • 順序違反はWCAG 2.4.3の重大なエラー、ユーザーはコンテキストを失いシナリオを完了できない
  • VoiceOver/TalkBackのジェスチャー、ハードウェアキーボード、自動シナリオでフォーカスをテスト

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください