2016年9月28日(水)にヤフー株式会社さんのセミナールームで開催された「アクセシビリティやるぞ!夏祭り2 ~俺たちにテストさせろスペシャル~」のセッション2、「セッション2 俺たちにテストさせろ!スマホアプリのユーザーテスト生実況!」に登壇させていただきました。ご参加いただきましたみなさま、関係者のみなさま、ありがとうございました。
セッションで私が提供したブログリーダーアプリ(Junnama Online)は私がiOSアプリ開発を学ぶ過程で作り始めたもので、「これはシンプルなアプリだな」と思われた方も多いのではないかと思います。おそらく参考書(私は「本気ではじめるiPhoneアプリ作り Xcode 7.x+Swift 2.x対応 黒帯エンジニアがしっかり教える基本テクニック」を読みました)の内容を応用して作っていけば、初心者でもなんとか完成させることができる簡単なアプリです。また、HTMLでWebページを作る場合と似ていて、標準のコンポーネントを適切に利用すればそれだけでもかなりアクセシブルな状態になる印象もありました。ただ、それだけでは少し不足だなと考えさせられる点も多かったように思います。
そこで今日は、アプリ制作中にアクセシビリティについてどのような事を考えたか、またどのような実装をしたか、技術的な側面をご紹介したいと思います。
お気に入りに登録ボタン・未読にするボタンの工夫
記事詳細ページに配置したお気に入りに登録ボタン・未読にするボタンについてです。
画像のラベル
ボタンに画像を設定した際、画像だけ指定すると画像のファイル名が読み上げられてしまいますので、ラベルを付けることが必須です。Interface Builderで画像とTitleを一度に指定すると警告が表示されたので、readStatusImage.accessibilityLabel = "未読にする"
のように、accessibilityLabel
でラベルの設定を行いました。
Xcode付属のAccessibility Inspectorで確認すると、ラベルの内容が反映されていることが分かります。(ラベルを付けていないと画像ファイル名になります。)
選択状態の表現
選択状態をビジュアルだけでなくプログラム的に表現できるか?ということを考えました。
今回はUIButtonで実装していたので、addClipButton.selected = true
のようなコードでステートを変更するだけで画像は選択状態の画像に変わり、VoiceOverの読み上げも「選択中の」と読み上げられるようになりました。
独自UIコンポーネントの改良
画面上部のカテゴリが並んでいる部分は、iOSライブラリ管理ツール「CocoaPods」で公開されている「PageMenu」を利用しました。デザインに手を加えると「グノシー」や「SmartNews」のタブのようになります。
UIScrollViewを利用した実装になっているので完全に独自のUIコンポーネントではないですが、標準のUIでもないのでVoiceOverで読み上げさせると各アイテムのラベルしか読み上げられず、どのようなUIなのか?、今何が選択されているのか?、が伝わらないのではないかと考えました。
そこで、まずaccessibilityLabel
を使用してタブであること、アイテムの総数、現在フォーカスしているアイテムが何項目目か、を示すことにしました。ちなみに、文中にカンマを入れるとVoiceOverが読み上げられる際、一呼吸置くようになります。
menuItemView.titleLabel!.accessibilityLabel = "\(controller.title!), タブ, 全\(controllerArray.count)項目中の\(Int(index) + 1)項目目"
次に、選択状態にあるタブは「accessibilityTraits
」を使用して選択状態にあることを示しました。(ボタンではないのでselected = true
は使えません。)
menuItems[currentPageIndex].titleLabel!.accessibilityTraits = UIAccessibilityTraitSelected
以上の設定により、例えばカテゴリ「アクセシビリティ」が選択状態にある場合は「選択中の, アクセシビリティ, タブ, 全11項目中の3項目目」のように読み上げられるようになります。
このUIは操作が難しいかもしれないという懸念があったのですが、VoiceOverがオンになっている時は左右のフリックでフォーカスが前後に移動するようで(これは初めて知りました)、中根さんは特に戸惑うことなく操作されていました。
UITableViewCellの工夫
記事一覧を表示しているUITableViewCellについてです。
AccessibilityTraitsの設定
UITableViewCellは、通常セルの内容のみが読み上げられます。ここで例えばAccessoryの設定を「Disclosure Indicator」にするとビジュアル的には「>」のようなアイコンが付き、VoiceOverも「ボタン」と読み上げるようになります。ただ、Disclosure Indicatorについて調べると、「Table View Programming Guide for iOS」に次のような説明がありました。
You use the disclosure indicator when selecting a cell results in the display of another table view reflecting the next level in the data model hierarchy.
他のアプリを見ると、Accessoryの設定を「Disclosure Indicator」にしていると思われるものと、何も設定していないもの、どちらも見られました。今回はガイドに従ってAccessoryの設定をNoneにしました。ただこれだと何らかの操作ができるセルなのか否かが伝わらないので、accessibilityTraitsをbuttonに設定しました。
VoiceOverでは操作可能なセルであることが分かるようになりましたが、やはりビジュアル的にも操作可能なセルであることが何かしらの方法で分かる方が良いかと考えています。
accessibilityLabelの設定
以前書いた記事「自作iOSアプリをVoiceOverで操作してみた」でも紹介したのですが、セルが自分の考えたとおりの順序で読み上げられないことがありました。また、今回は純正の「メール」アプリや「Reeder」アプリに倣って○の画像で未読状態を表現していたため、それをビジュアル以外でも伝わるようにする必要がありました。
以前にも紹介しましたが、Appleのサイトにある「iOSアクセシビリティ プログラミングガイド(PDF)」の「Table Viewのアクセシビリティの改善」の項に次のようにあります。
Table Viewの行ごとに複数の情報を表示する場合、情報を1つの理解しやすいラベルに集約することでVoiceOverユーザの体験を改善することができます。
テーブルに、それぞれ大量の情報を提供するセルが含まれている場合は、これらの中からの情報を組み合わせてLabel属性にすることを検討するべきです。こうすれば、VoiceOverユーザはセルの内容の意味を1つのジェスチャで得ることができ、個々の情報にそれぞれアクセスする必要がなくなります。
そこで、今回は「未読(未読の場合のみ), 記事タイトル, 公開日時」をaccessibilityLabel
で設定しました。(余計なことかもしれませんが、時間が12:00のような表示形式だったので、聞いて分かりやすいように少し編集しました。)
var a11yLabel = ""
if didRead {
a11yLabel = ""
} else {
a11yLabel = "未読, "
}
a11yLabel += item.title + ", "
a11yLabel += dateText.stringByReplacingOccurrencesOfString("\\:(.*)", withString: "時$1分", options: NSStringCompareOptions.RegularExpressionSearch, range: nil)
cell.accessibilityLabel = a11yLabel
モーダル表示(UIPresentationController)の実装
旅行キュレーションメディア「RETRIP」のアプリをはじめ、いろいろなアプリ、またiOSの設定画面などで見られる実装ですが、コンテンツの上を半透明の黒で覆い、モーダルでコンテンツを表示する実装方法があります。
このカスタムモーダルはUIPresentationControllerで実装ができます。しかし、VoiceOverを有効にして画面を操作してみると、コンテンツを覆う半透明の黒のレイヤーを入れた場合になぜか隠しているつもりの後ろのコンテンツ(PresentingView)のテキストやボタンにフォーカスが当たってしまいます。(左右のフリックではなく手当たり次第に画面を触った場合。)App Storeで公開されている他のアプリでも同じ現象が見られました。
デベロッパープログラムのTechnical Support Incidentを利用してAppleに解決方法を尋ねたところ、accessibilityViewIsModal
プロパティを操作すればよいことが分かりました。早速記述をしたところモーダルの外のコンテンツにはフォーカスしなくなりました。
ただし、accessibilityViewIsModal
が効くのはiOS 10からだそうです。iOS 9以下では、accessibilityElementsHidden
プロパティを操作すればよいようです。
class CustomPresentationController: UIPresentationController {
lazy var dimmingView :UIView = {
let view = UIView(frame: self.containerView!.bounds)
view.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
view.alpha = 0.0
return view
}()
override func presentationTransitionWillBegin() {
guard
let containerView = containerView,
let presentedView = presentedView(),
let presentingView = presentingViewController.view
else {
return
}
// Add the dimming view and the presented view to the heirarchy
dimmingView.frame = containerView.bounds
containerView.addSubview(dimmingView)
containerView.addSubview(presentedView)
// Fade in the dimming view alongside the transition
if let transitionCoordinator = self.presentingViewController.transitionCoordinator() {
transitionCoordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext!) -> Void in
self.dimmingView.alpha = 1.0
}, completion: { context in
containerView.accessibilityViewIsModal = true
presentingView.accessibilityElementsHidden = true // iOS 9 workaround
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil)
})
}
}
override func dismissalTransitionWillBegin() {
guard
let presentingView = presentingViewController.view
else {
return
}
// Fade out the dimming view alongside the transition
if let transitionCoordinator = self.presentingViewController.transitionCoordinator() {
transitionCoordinator.animateAlongsideTransition({(context: UIViewControllerTransitionCoordinatorContext!) -> Void in
self.dimmingView.alpha = 0.0
}, completion: { context in
presentingView.accessibilityElementsHidden = false // iOS 9 workaround
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil)
})
}
}
Dynamic Type機能のサポート
iOSの設定画面で文字サイズを変更することができます。ここで設定した情報が反映されるよう、Dynamic Type機能をサポートしました。ユーザーの方は自分の好みのテキストサイズで記事を読むことができます。
先に紹介した「PageMenu」で実装したカテゴリ一覧の部分の文字サイズが変わらないのが目下の課題です。
おわりに
今までにWebサイトのアクセシビリティについて学んでいたので、今回のアプリでもその知識を活かして「ここは何か追加でコードを書かなければならないのでは...?」と直感的に思うことがありました。「iOSアクセシビリティ プログラミングガイド(PDF)」が用意されていたこともあり、iOSアプリのアクセシビリティ向上も私自身は取り組みやすかったと感じています。
動きがあるアプリ、また例えばセルの上でフリックするとさまざまな操作ができるような実装のアプリだとどのような実装が必要なのか気になるところですが、そのようなアプリを制作した際にはまたご紹介したいと思います。