アプリをVoiceOverに対応しよう!
(とは言っても、対応できるアプリは限られると思います。)
iOSアプリ開発とアクセシビリティ対応について|Technology-Gym
こちらの記事を参考にすると、できます!
のですが、実際に対応してみた感じを、今後対応する方の参考になるかなと載せておきます。
上記サイトと重なりますが、各種リンクを載せておきます。
【 Apple 】
アップル – アクセシビリティ
iOSアクセシビリティ プログラミングガイド
【 参考サイト 】
iOSのVoiceOver対応開発 slideshare
VoiceOver(ボイスオーバー)機能って???
まずボイスオーバー機能ってどんななのか簡単に説明すると、
『画面上の部品の、名前と、それが何を意味するのかを、声で教えてくれる機能』
です。
画面上で指をスライドさせると、画面上の、指を置いている部分になにがあるのかを教えてくれます。
画面を見なくても、読み上げてくれる説明だけで、操作ができます。
でも、操作対象のアプリがVoiceOver機能を考慮していないと、指を置いている部分になにがあるのかは、うまく伝わりません。
VoiceOver機能を考慮する?
VoiceOver機能を考慮したアプリにする場合は、画面上の部品それぞれに、名前や説明などを追加しておきます。
ボタンにちゃんと名前があると、
「せっていぼたん」「めーるさくせいぼたん」みたいに読み上げてくれます。
名前が無いと、
“画像ファイル名”ぼたん と読み上げられます。
画像ファイルが、btnSetting.pngだったりすると、
「びったじたぬー ぼたん」な雰囲気で読まれます(聞き取れない)。
これは衝撃の事実です。
なので、適切な名前、説明を追加してあげないといけない。
ボタンやラベルは上記でいいとしても、
画面の構成が動的に変化する場合など、
『動的なメッセージの通達は、自由にできない』
動的になにかを伝えたい場合は、専用の通知がありました。
(コメントでの指摘感謝です。以下内容を変更してあります。)
プログラミングガイドにも一部載っていました。
そんな通知に関して少し書いておきます。
通知の一覧は、
Xcodeドキュメント
UIAccessibility Protocol Reference の一番下に羅列されています。
Viewの変更時、
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @"読んでほしい内容");
で、”読んでほしい内容”を読み上げてくれますが、
ドキュメントでは、レイアウト変更時は、
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, @"message");
この通知が用意されていました。
この通知は、システムへ通知するためのもの(らしい)ので、キーボードを変更したり、セルを削除したりな時に利用するのかもしれない。
後述するインスペクター上では、この通知内容を確認できましたが、ユーザーには何も伝えれませんでした。
(あまり試せてはいません)
Viewの変更時、
UIAccessibilityScreenChangedNotificationは、内容をそのまま読んでくれますので、この通知がよさそうです。
Viewの変更ではなく、単にメッセージを投げたい場合には、
UIAccessibilityAnnouncementNotificationを利用すべし、との記載があります。こちらもそのまま読み上げてくれました。
なにかしら処理が終わった、メールを送信した、みたいな場面では、ダイアログを表示するより、このメッセージだけが良いのかもしれない。
UIActionSheetや、UIAlertViewを表示する場合は、iOS側で説明をしてくれますが、そういったiOSのAPIではなく、カスタムUIViewなどを単に表示させる場合など、自分で読み上げてほしいメッセージを用意する必要があります。
たとえば、
ボタンをタップすると、そのボタンが拡張されたり、他のボタンが表示される場合。
ボタンタップ後に、
「いくつかのボタンが表示されました。」
みたいなメッセージを、動的に伝えることはできますが、そんなメッセージを伝えたとしても、
ボタン選択時、毎回読み上げられます。
毎回伝える必要もなく、そして読み上げも長くなります。
あんまりよろしくないですね。
VoiceOverを判断して分岐させる
今回対応を行ったアプリは、
予定リスト -カウントダウン
予定までのカウントダウンを行うアプリです。
VoiceOver利用者の方から要望を頂いたのは、このアプリでした。
設定した複数の予定が、動的にカウントダウンされます。
表示されている予定をタップすると、
その予定セルの、下部が拡張されて、複数のボタンが表示されます。
画面を見ていないと、この状態変化はわかりません。
画面上には存在するため、画面上で指をスライドさせると、このボタンはわかりますが、
元々無かったボタンがなぜか表示されている状態 となってます。
(そしてボタンの名前も設定していなかったので、謎が深っています。)
セルの名称やメッセージを追加したとしても、きっとわかりづらい。
ボイスオーバー時は、処理を変えることにしました。
設定を判断する関数があるので、単純に分岐ができます。
if(UIAccessibilityIsVoiceOverRunning()){ //ボイスオーバー中 }else{ }
セルの拡張ではなく、単にメニュー(ActionSheet)を表示させるようにしました。
VoiceOverでなくても、こっちのほうがわかりやすいですね。。
ここはちょっと大きな変更でしたが、どうにもできない部分は、
UIAccessibilityIsVoiceOverRunning()
を使っての分岐で対応ができます。
ただ、シミュレーターにはVoiceOver機能自体が付いていないので、この分岐の確認はできません。
その代わりに、アクセシビリティ・インスペクター機能が付いてます。
シミュレーターの
設定 > 一般 > アクセシビリティ
ONにすると、クリックした部品のアクセシビリティな設定値を確認できます。
ボタンやラベルに名前と説明を!
そんなボタンやラベルのアクセシビリティ設定値、部品の名前や説明(ヒントメッセージ)を設定するには。
InterfaceBuilderの場合は、割愛。
ではなく、このプロジェクトでは使っていなかったので、触っていません。
一番最初に紹介したリンク先などを参照していただけると助かります。
システムIDを利用したボタンは、もしかすると何も設定はいらないかもしれません。
UIBarButtonSystemItemEditなどの値を利用したボタン。
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(startEditing:)]
この場合は、名前が「編集ボタン」と自動的に設定されています。
『ラベル』が、この部品の『名前』です。
この編集ボタンの場合は、VoiceOverでフォーカスを当てた際に、
「へんしゅう ぼたん」と読まれます。
initWithTitle、UIBarButtonItemStyleBorderedで、タイトルを指定すると、
[[UIBarButtonItem alloc] initWithTitle:@”Info” style:UIBarButtonItemStyleBordered target:self action:@selector(openSettings:)]
この場合は、たまたま「Info」を、「いんふぉ」 と読んでくれたので、
「いんふぉ ぼたん」
と読まれました。
これはこのままにしたのだけど、表示とラベルを別名にする場合は、
そのボタンの、accessibilityLabelプロパティに文字列を設定します。
infoButton.accessibilityLabel = @"情報";
これで実行すると、ラベルが変更され、
「じょうほう ぼたん」
と読まれます。
UILabelに関しても同様で、
この場合は、
「★」は「あすたりすく」と読まれてしまうので、表記とaccessibilityLabelは別に文字列を設定しています。
他にも、「・」は「なかぐろ」と読まれたり。
やっかいなのが、セル内容。
セル内に複数のViewを配置して、内容を表示していますが、これはこのまま上から順に読まれました。
前述の通り、★は「あすたりすく」だったりなのと、日は「ひ」だったり。
残り時間を表示しているので、9じかん30ぷん と読んでほしい。
以下のヒントメッセージの方法は不具合を誘発させたため、真似しないでください。(2014.5.14)
このセルに関しては、
全部品のアクセシビリティ無効化し、ヒントメッセージに、読んでほしい文字列を設定しました。
なぜヒントに???
セルのaccessibilityLabelに設定した内容は、セルが更新されると、更新の度に読み上げられます。
通常は、セルのaccessibilityLabelに設定すべきです。
このアプリ特有ですが、セル内容は、動的に更新されていきます。
触ってもいないのに、読み上げがループしてしまうんですよね。
ヒントは、再選択や、移動をしない限り、一度しか読まれません。
通常、ヒントが設定されていると「○○ボタン ヒント内容」のように続けて読んでくれます。
iPhone設定で、ヒントを読む/読まない設定がありますが、
この設定に関わらず、ラベルが無い場合は、ヒントが読まれました。
セルのアクセシビリティ有無を設定し、
[self.contentView setIsAccessibilityElement:YES]; //ラベルのアクセシビリティを無効化 [self.lblTitle setIsAccessibilityElement:NO]; [self.lblDate setIsAccessibilityElement:NO]; ....
accessibilityHintプロパティに、読み上げてほしい内容を設定。
self.contentView.accessibilityHint = 読み上げてほしい内容
こうすることで、ヒントの内容だけ読み上げてくれるので、いい感じに伝えれます。
(通常とは異なるヒントの使い方なので、あまりオススメはしません。)
上記の方法では、iOSの不具合(多分)を誘発させました。
以下の方法が無難です。
見えないUILabelを貼っておき、そこに読み上げたい内容をセットします。
UILabel *voiceView; 〜〜〜 //initWithStyle voiceView = [[UILabel alloc] initWithFrame:CGRectMake(1,1,1,1)]; [voiceView setBackgroundColor:[UIColor clearColor]]; [self.contentView addSubview:voiceView]; //setIsAccessibilityElement:YESをやってはいけない。 //[voiceView setIsAccessibilityElement:YES]; //contentViewに対して、setIsAccessibilityElementを操作してはいけない //[self.contentView setIsAccessibilityElement:YES]; 〜〜〜 //データ更新時 //読み上げたい内容を設定 voiceView.accessibilityLabel = @"読み上げたい内容";
setIsAccessibilityElement:NO はいいのだけど、明示的なYESは現状問題が発生します。
VoiceOver時、何故かテーブルのスクロールがうまく動作しなくなります。
(2014.5.14 追記)
このアプリは特殊だったかもしれませんが、タイマー、ストップウォッチアプリなど頻繁に表示が更新されるViewには、AccessibilityElementの特徴(Trait)に、UIAccessibilityTraitUpdatesFrequentlyを利用するようです。
更新毎回の読み上げではなく、数秒ごとの読み上げになるのかな。
実際にUIAccessibilityTraitUpdatesFrequentlyを設定し、インスペクターで見ると、
特徴 頻繁にアップデート
となっていました。
こういったAccessibility Traitsは、各Viewごとに持っていて、
UIButtonの場合は、UIAccessibilityTraitButtonなどが設定済みです。
設定する場合は、
accessibilityTraitsプロパティを変更します。
この一覧も、UIAccessibility Protocol Referenceドキュメント内にあります。
accessibilityLabelとaccessibilityHintの設定だけでも、VoiceOverでのアプリの印象は、かなり変わるはずです。
この2つのプロパティと、アクセシビリティアイテムにするかしないかの
isAccessibilityElementプロパティ
この3つで、ほぼ実装はできるんじゃないかなと、感じますが、中には予想を裏切ってくれるViewがいました。
タッチできないボタンアラワル
このアプリではないのだけど、カスタムUIViewをボタンとして利用している場合に、そのボタンは、VoiceOver下で、居ないものとして扱われました。
指をそのボタンに合わせても、なにも反応しない。
そこには何も無い。
これはショッキングでした。
長くなったので、この内容は、次の記事に続けることにします。
※なかなか時間が取れなくてまだ続きは書けておりません。
コメント
[…] [VoiceOver] iOSアプリのVoiceOver対応の実装 いち タグ: ios, iPhone, VoiceOver, 対応 […]
以下の処理で、画面が変化したことをVoiceOverで知らせることができますよ。
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @”ほげほげ”);
もっとも、今回のケースではActionSheetを使う方が良い解決方法だと私も思います。
>sawatさん
おお!
ここは思い込みで書いてしまっていましたね。
情報ありがとうございます!
少し試してみて、内容を書き換えておきます。