😸

(SwiftUI)ShapeまたはPathで図形を描画した場合のVoiceOverヒットエリアについて

2024/06/25に公開

結論

お急ぎの方のため、先に結論から述べます。

  1. ShapeまたはPathで任意の形の図ケオを描画するとき、含まれる図形が1つであればVoiceOverヒットエリアはその形状になります。例えば三角形を描画すればフォーカスを示す枠線が三角形で表示されます。
  2. 2つ以上の図形が1つのShapeまたはPathに含まれる場合、VoiceOverヒットエリアはそれらの図形を囲む最小の四角形(bounding rectangle)をヒットエリアとします。
  3. ドーナッツのような中心がくり抜かれた図形をeven-odd fillで描画する場合も外側のパスと内側のパス合計2つの図形を描画しているものと判定されてVoiceOverヒットエリアは四角形になります。

はじめに

iOSに搭載されているスクリーンリーダー、VoiceOverの話題です。VoiceOverを起動した状態でテキストやボタンをタッチすると、そのUI要素を囲む四角形の枠線が表示された後に読み上げが開始されます。

さて、UI要素囲む四角形の枠線、つまりVoiceOverのヒットエリアですが、実は任意の図形に設定することができます。例えばiPhone・iPadのロックスクリーンに表示されるピンコード入力用の数値ボタンはVoiceOverヒットエリアが円形になっています。

なお、図形は円に限らず三角形や星形など一筆書きで描画できる図形であれば何でもヒットエリアとして設定可能です。この記事は利便性の低いそのようなUIを推奨するものではありません。

実験

実際にVoiceOverヒットエリアの形を変化させてみましょう。ここでは例として、直角三角形のVoiceOverヒットエリアを作成します。

正方形を作成し、左上から右下に向かって対角線を引きます。そうすると2つの三角形が出来上がります。この三角形それぞれにヒットエリアを設定します。

Shapeの場合、.fill()や.stroke()でレンダリングした後に.accessibilityLabel() modifierを設定するとVoiceOverヒットエリアは図形の形状に一致した形となります。

import SwiftUI

struct LeftTriangle: Shape {
  func path(in rect: CGRect) -> Path {
    var path = Path()

    path.move(to: CGPoint(x: 0, y: 0))
    path.addLine(to: CGPoint(x: 0, y: 300))
    path.addLine(to: CGPoint(x: 300, y: 300))
    path.closeSubpath()

    return path
  }
}

struct RightTriangle: Shape {
  func path(in rect: CGRect) -> Path {
    var path = Path()

    path.move(to: CGPoint(x: 300, y: 0))
    path.addLine(to: CGPoint(x: 300, y: 300))
    path.addLine(to: CGPoint(x: 0, y: 0))
    path.closeSubpath()

    return path
  }
}

struct ContentView: View {
  var body: some View {
    VStack {
      Text("VoiceOver hit area testing")
      ZStack {
        LeftTriangle()
          .fill(Color.gray)
          .accessibilityLabel("Left Triangle")
        RightTriangle()
          .fill(Color.black)
          .accessibilityLabel("Right Triangle")
      }
      .frame(maxWidth: 300, maxHeight: 300)
    }
  }
}

いつVoiceOverヒットエリアの形状を変化させるべきか

あくまで一人のVoiceOverユーザーの意見ですが基本的にヒットエリアの形状を任意の図形に変化させるべきではありません。四角形がベストです。なおUIの操作しやすさを向上させるためヒットエリアを広げるのは問題ありません。

理由は単純で、ヒットエリアが四角形でない場合の操作性が低下するためです。先ほど述べたロックスクリーンのピンコード入力ボタンを例にすると、VoiceOverを有効にした状態で数字の1と4の中間を指でなぞってください。場所は画面の左端です。

ボタンのVoiceOverヒットエリアが円形のため、空白地帯ができています。ここに指を滑らせて、数字の4に近い場所で指を離すと、VoiceOverカーソルは数字の1に移動します。指を離した場所の最も近くにあるUIにVoiceOverカーソルが移動してほしいところですが、現状そのような挙動にはなっていません。

VoiceOverの挙動が今後のOS更新とともに変化する可能性はあります。しかし、そもそもヒットエリアの空白地帯を作らせないようにUIを実装するべきです。数値入力用テンキーUIであれば、見た目は丸いボタンで描画しつつ、ヒットエリアは四角形で空白地帯が生まれないように実装するべきです。

Discussion