ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ダイナミックタイプの導入
ダイナミックタイプは、システム全体およびすべてのアプリを対象に、ユーザーが好みのテキストサイズを選択できるようにする機能です。このセッションでは、ダイナミックタイプのサポートを導入するための基本事項をご紹介します。ダイナミックタイプの仕組み、アプリにおけるテキストサイズの変更に関する問題を見つける方法、SwiftUIとUIKItを使用して優れたダイナミックタイプの体験を実現するための実践的なステップを確認しましょう。ラージコンテンツビューアを使用して、ナビゲーションコントロールをすべてのユーザーが利用できるようにする方法もご紹介します。
関連する章
- 0:00 - Introduction
- 3:11 - Scaled text
- 6:00 - Dynamic layouts
- 8:56 - Images and symbols
- 11:58 - Large content viewer
リソース
-
ダウンロード
こんにちは Gaethです Accessibilityチームのエンジニアです Appleは アクセシビリティに関する あらゆるニーズに配慮しています 多くのコンテンツでは 情報伝達の手段は テキストであるため 本セッションの内容は 視覚的アクセシビリティにおいて 重要な意義を持ちます 具体的には ユーザーがアプリ内でテキストを読み 操作する体験を ダイナミックタイプにより すべての人にとって優れたものにする 方法についてお話しします ダイナミックタイプを使うと システム全体とアプリにおいて ユーザーが テキストサイズを選べるようになります みなさんのアプリを使う多くのユーザーが この設定をカスタマイズします この機能をサポートすることは アプリが提供する価値を 最大限に活用してもらう上で 非常に重要です より大きなテキストサイズでの アプリの 動作を考慮したことがない方はぜひ 本セッションを参考にしてください
ダイナミックUIを利用して構築した インターフェイスは 画面のサイズや向き および プラットフォームを問わず機能します これはとても重要なことです なぜなら テキストサイズに関する 好みやニーズは 人によって異なるためです ダイナミックタイプでは あらゆるサイズの テキストが読みやすくなります ユーザーは 自身のニーズに合わせて テキストサイズを選び 変更できます 大きいテキストサイズでの アプリの動作を確認したことがない方は アクセシビリティ設定にアクセスし ぜひ試してみてください テキストサイズをカスタマイズするには アクセシビリティ設定の で を選択します デフォルトでは テキストサイズを 7段階から選べるようになっています をオンにすると サイズをもう5段階大きくできます テキストサイズのコントロールを コントロールセンターに追加しておけば 好みのサイズにすばやく変更できます
デフォルトのテキストサイズはLargeです
テキストサイズを変更すると すぐに変更が反映されます この例の設定アプリの画面では テキストサイズを大きくしたことで すべてのテキストのサイズが 自動で大きくなりました タイトル 本文 各セルのラベルに 表示されているテキストなどです このテーブルビューのレイアウトも 大きくなったコンテンツに合わせて 各サブビューのサイズが大きくなっています アクセシビリティ設定で さらに大きな テキストサイズを選択すると コンテンツが ディスプレイの境界を越えるほど 大きくなっていますが スクロールすればすべてを読めます このセッションでは ダイナミックタイプを使って アプリのコンテンツを拡大し 最高の体験を提供できるよう 調整するための方法をいくつかご紹介します まずは ダイナミックタイプの 使い方について説明します 次に 大きなサイズに合わせて レイアウトを調整する方法をご紹介します また インライン画像や記号のための オプションを確認します 最後に解説するのは ラージコンテンツビューアを使って ほかのコンテンツに合わせて拡大できない コントロールを 読みやすくする方法です
まず テキストサイズの変更方法の説明です ダイナミックタイプにより アプリの体験を向上させる第一歩は 組み込みのテキストスタイルを 活用することです 固定フォントではなく システムに用意されているスタイルから テキストを選ぶことができます 例えば テキストスタイルを 「本文」にすると 複数行のテキストも 読みやすくなります 「ヘッドライン」スタイルにすると 見出しを ほかのコンテンツと区別しやすくなります これらのスタイルを使うと コンテンツの視覚的階層を維持したまま デバイスでユーザーが選択した サイズに合わせて アプリのテキストが自動的に調整されます
組み込みのテキストスタイルの指定には SwiftUIでfontモディファイアを使います 例えば 「タイトル」スタイルを選ぶには titleパラメータを渡します UIKitでテキストスタイルを指定するには UILabelの adjustsFontForContentSizeCategory プロパティをtrueに設定すれば システムの テキストサイズに応じてラベルの フォントも自動更新されるようになります 次に preferredFont(forTextStyle)を使って フォントを好みのスタイルに設定します labelのnumberOfLinesを0にすると テキストの表示行数が テキストの量に応じて調整されるので テキストが途切れずに表示されます アプリでのダイナミックタイプの使用を 検討する際は 次のような点に注意してください テキストサイズを大きくした時に 必要な行数が表示できないと テキストが途中で切れてしまいます 表示スペースの枠が固定だと テキストが 一部しか表示されない場合があります 幸い このような問題は簡単に検出できます そのための最適なツールの1つが Xcodeプレビューです SwiftUIをお使いの場合は Xcodeで プレビューキャンバスを表示し バリアントのボタンをクリックして を選択します
選択可能なテキストサイズの各バリアントを Xcodeでプレビューできるので 特定の表示に問題がある場合 すぐに気づけます または Xcodeのキャンバスで 設定のボタンをクリックして 特定のサイズを選択することもできます Xcodeのデバッグ機能を使って テキストサイズをテストする方法もあります 設定アイコンをクリックすると ダイナミックタイプとアクセシビリティの その他の設定をオーバーライドできます アプリの監査を実行し アクセシビリティの 問題がないか確認することもできます 監査によってアプリのビュー階層を検証し アクセシビリティに関する 様々な問題をチェックして 切り取られたテキストやラベルの欠落 低いコントラスト比などを検出できます
ダイナミックタイプの導入は まず システムフォントのテキストスタイルから 始めるとよいでしょう さらに大きいサイズのテキストを使って アプリ体験を向上させるにあたり 最適な体験を提供できるよう コンテンツの レイアウトの調整も検討しましょう 例えば 連絡先アプリで 新しい連絡先ポスターを作成する際 ポスターのオプションは 横方向スタックに表示されますが テキストサイズを大きくすると レイアウトが縦方向スタックに変わり 各セルがディスプレイの 幅いっぱいに表示されます
このような場合 大きなテキストに合わせて レイアウトを変更させず フォントが デフォルトまたは小さいサイズの時の レイアウトを維持することもできます
このアプリには4つの画像が 横方向スタックに配置されており 各画像の下に Standing FigureやRolling Figureなどの ラベルが表示されています このラベルは デフォルトの大きなサイズでも 読めるかもしれませんが アクセシビリティ設定の大きなサイズに すると 表示が崩れてしまいます これは 各画像の幅が狭く スペース内にラベルが収まらないためです セルを一つずつ見ていきましょう FigureCellはVStackで 画像とタイトルが含まれています SwiftUIで動的なレイアウトを実装するには EnvironmentのKeyPathを dynamicTypeSizeにします 次に AnyLayout型のプロパティとして dynamicLayoutを指定します これで アクセシビリティ設定の テキストサイズには HStackLayoutが ほかのテキストサイズには VStackLayoutが適用されます 次に 本文のレイアウトを VStackからdynamicLayoutに更新します あとは セルを縦方向に配置するだけです これらを含むビューで 同じ手順で メインコンテンツのビューを設定し アクセシビリティ設定のテキストサイズでは VStackLayoutになり それ以外の場合は HStackLayoutになるようにします
いいですね これで テキストがアクセシビリティ設定の サイズに変更されると レイアウトが動的に変更されて テキストの表示幅が確保されるので テキストが途切れず かつ 読みやすくなっています
これをUIKitで実現するには UIStackViewを使います StackViewには サブビューを 縦または横方向に配置するために 必要なロジックがすべて含まれており 軸プロパティの更新のみで適用できます
軸を定義するには ビューコントローラのtraitCollectionの preferredContentSizeCategoryで isAccessibilityCategoryプロパティを 使用します
アプリ実行中のテキストサイズ変更に 対応できるようにするには UIContentSizeCategoryの didChangeNotificationにサブスクライブし スタックビューの軸を 最適な方向に更新します 大きなサイズのテキストを使う時は レイアウトだけではなく テキストと一緒に表示される画像や記号にも 調整が必要な場合があります テキストを大きくする時の 画像やアイコンの処理では テキストに合わせてアイコンを拡大した際に テキストのスペースが 狭くなり過ぎないように バランスを調整することが重要です 例えば iPhoneの設定アプリには テーブルビューの各項目に テキストラベルと装飾画像が含まれています テキストサイズを大きくすると ラベルは大きくなりますが 装飾画像のサイズは変わりません
これは アプリでは装飾用のビューよりも 重要なコンテンツのサイズ変更が 優先されるためです 画像サイズが変更されない場合は スペースを活用できるよう テキストを 画像の次の行へ改行します テキストサイズを非常に大きくする場合 装飾的要素を削除することもできますが これは例外的なケースに限定すべきです 使用する場合 機能と重要なコンテンツに 影響が出ないよう 注意する必要があります では SwiftUIとUIKitで これを実装する方法を確認しましょう SwiftUIでは テキストを簡単に アイコンの次の行へ改行できます ViewをListに含めるだけで テキストを アイコンの次の行へ改行でき ほかの作業は不要です Listビュー以外でも 画像を直接テキスト内に 挿入することもできます UIKitでこの動作を実装するには NSAttributedStringを使用します AttributedStringを作成し それに NSTextAttachmentとして指定した 画像を追加します
画像のサイズも テキストに合わせて 調整されるようにすべきケースもあります 特に 画像にテキストが含まれる場合や コンテンツのほかの部分に関連する 重要なアイコンがある場合などです
画像がSF Symbolsなら サイズは自動的に調整されます しかし アセットに画像やPDFが 含まれる場合は 対応が必要です この場合 ScaledMetric APIを使えば 選択したテキストサイズに合わせて 画像のサイズが調整されるようになります ScaledMetricを追加して 画像の幅や高さを指定するだけで テキストサイズが変更されると 実行時にこの値が自動的に調整されます
素晴らしいです!テキストサイズを大きくすると 画像とテキストのサイズが 自動的に変更されます
UIKitでこの動作を実装する際に使うのは UIImage.SymbolConfigurationです 例えば SymbolConfigurationと textStyleを使って 特定のテキストスタイルの Configurationを作成し そのSymbolConfigurationを使って UIImageを作成します 最後に ラージコンテンツビューアについて 説明します ラージコンテンツビューアを使うと 大きなテキストサイズとともに 拡大されないコントロールも見やすくなります その機能の仕組みをご紹介します iPhoneの時計アプリには 画面下部にタブが4つあります このタブをタップし そのまま押し続けると 画面中央に ラージコンテンツビューアが表示され タップしているタブのラベルとアイコンが 大きなサイズで表示されます
スワイプでほかのタブに移動し 目的のタブが表示されたら 指を離します
この方法なら タブバーの高さが 画面の10%未満に収まります をオンにすると タブバーの高さも増える設定にしている場合 タブバーが 画面の約4分の1を占めてしまいます このバーは常に表示されているので アプリのコンテンツを 邪魔しないように表示することが大切です システムのデフォルトの コントロールバーを使っている場合は 特に何もする必要がありません これはすでにサポートされています しかし カスタムの バーやビューを使用する場合は ラージコンテンツビューアを 必要に応じて利用できるようにしましょう
SwiftUIでは カスタムのタブバーを この例のような形で構築できます この例では 表示される各タブのボタンが HStackに含まれています バインディングにより タブバーが 選択された項目を追跡できるようになります
ラージコンテンツビューアのサポートを 追加するには accessibilityShowsLargeContentViewerモディファイアを使って ビューに表示する ボタン名や記号などのラベルを指定します
ラージコンテンツビューアを UIKitでサポートするには UILargeContentViewerItemプロトコルに ビューを準拠させて タイトルや画像を指定するプロパティと ラージコンテンツビューアを表示する タイミングを示すプロパティを指定します 次に UILargeContentViewerInteractionの インスタンスを作成して ビューに追加します コントロールで独自の ジェスチャリコグナイザを使う場合は 追加の処理を行って まず ラージコンテンツビューアがジェスチャを 処理できるようにする必要があります
ほかのジェスチャリコグナイザとの 認識や失敗の関係を設定するには UILargeContentViewerInteractionの gestureRecognizerForExclusionRelationship について調べてみてください
ダイナミックタイプの導入方法の説明は 以上です さっそくご自身のアプリで 大きなテキストのサイズを試してみてください システム定義のテキストスタイルの使用や レイアウトのカスタマイズにより テキストの読みやすさの向上に注力し アプリを改善できる部分を検討しましょう テキストサイズに関する好みやニーズは ユーザーごとに異なります 各ユーザーのニーズに アプリが柔軟に対応できるようにすれば アプリでテキストを読む すべての人に優れた体験を提供できます さらに UIテストにアクセシビリティの 監査を追加すれば アプリを変更するたびに ダイナミックタイプに関する問題がないか 確認できます SwiftUIを活用した支援技術について 詳しく解説している 「Catch up on accessibility in SwiftUI」の セッションもぜひご覧ください ご視聴ありがとうございました
-
-
3:53 - Built-in text styles with SwiftUI
// Use built-in text styles with SwiftUI import SwiftUI struct ContentView: View { var body: some View { Text("Hello, World!") .font(.title) } }
-
4:06 - Built-in text styles in UIKit
// Built-in text styles in UIKit import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let label = UILabel(frame: .zero) setupConstraints() label.text = "Hello, World!" label.adjustsFontForContentSizeCategory = true label.font = .preferredFont(forTextStyle: .title1) label.numberOfLines = 0 self.view.addSubview(label) } }
-
7:20 - Dynamic layout in SwiftUI
// Dynamic layout in SwiftUI import SwiftUI struct FigureCell: View { @Environment(\.dynamicTypeSize) private var dynamicTypeSize: DynamicTypeSize var dynamicLayout: AnyLayout { dynamicTypeSize.isAccessibilitySize ? AnyLayout(HStackLayout()) : AnyLayout(VStackLayout()) } let systemImageName: String let imageTitle: String var body: some View { dynamicLayout { FigureImage(systemImageName: systemImageName) FigureTitle(imageTitle: imageTitle) } } }
-
7:52 - Dynamic layout in SwiftUI
// Dynamic layout in SwiftUI import SwiftUI struct FigureContentView: View { @Environment(\.dynamicTypeSize) private var dynamicTypeSize: DynamicTypeSize var dynamicLayout: AnyLayout { dynamicTypeSize.isAccessibilitySize ? AnyLayout(VStackLayout(alignment: .leading)) : AnyLayout(HStackLayout(alignment: .top)) } var body: some View { dynamicLayout { FigureCell(systemImageName: "figure.stand", imageTitle: "Standing Figure") FigureCell(systemImageName: "figure.wave", imageTitle: "Waving Figure") FigureCell(systemImageName: "figure.walk", imageTitle: "Walking Figure") FigureCell(systemImageName: "figure.roll", imageTitle: "Rolling Figure") } } }
-
8:20 - Dynamic layout in UIKit
// Dynamic layout in UIKit import UIKit class ViewController: UIViewController { private var mainStackView: UIStackView = UIStackView() required init?(coder: NSCoder) { super.init(coder: coder) NotificationCenter.default.addObserver(self, selector: #selector(textSizeDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil) } override func viewDidLoad() { super.viewDidLoad() setupStackView() } @objc private func textSizeDidChange(_ notification: Notification?) { let isAccessibilityCategory = self.traitCollection.preferredContentSizeCategory.isAccessibilityCategory mainStackView.axis = isAccessibilityCategory ? .vertical : .horizontal setupConstraints() } }
-
10:12 - Scale inline images with SwiftUI
// Inline images in SwiftUI import SwiftUI struct ContentView: View { var body: some View { List { FigureListCell(figureName: "Standing Figure", systemImage: "figure.stand") FigureListCell(figureName: "Rolling Figure", systemImage: "figure.roll") FigureListCell(figureName: "Waving Figure", systemImage: "figure.wave") FigureListCell(figureName: "Walking Figure", systemImage: "figure.walk") } } }
-
10:30 - Scale inline images with UIKit
// Inline images in UIKit func attributedStringWithImage(systemImageName: String, imageTitle: String) -> NSAttributedString { let attachment = NSTextAttachment() attachment.image = UIImage(systemName: systemImageName) let attachmentAttributedString = NSMutableAttributedString(attachment: attachment) attachmentAttributedString.append(NSAttributedString(string: imageTitle)) return attachmentAttributedString }
-
11:05 - Scale images in SwiftUI
// Scaling images in SwiftUI import SwiftUI struct ContentView: View { @ScaledMetric var imageWidth = 125.0 var body: some View { VStack { Image("Spatula") .resizable() .aspectRatio(contentMode: .fit) .frame(width: imageWidth) Text("Grill Party!") .frame(alignment: .center) } } }
-
11:38 - Scale symbols with UIKit
// Symbol configuration in UIKit import UIKit func imageWithBodyConfiguration(systemImageName: String) -> UIImage? { let imageConfiguration = UIImage.SymbolConfiguration(textStyle: .body) let configuredImage = UIImage(systemName: systemImageName, withConfiguration: imageConfiguration) return configuredImage }
-
13:15 - Add large content viewer support with SwiftUI
// Large content viewer support in SwiftUI import SwiftUI struct FigureBar: View { @Binding var selectedFigure: Figure var body: some View { HStack { ForEach(Figure.allCases) { figure in FigureButton(figure: figure, isSelected: selectedFigure == figure) .onTapGesture { selectedFigure = figure } .accessibilityShowsLargeContentViewer { Label(figure.imageTitle, systemImage: figure.systemImage) } } } } }
-
13:45 - Add large content viewer support with UIKit
// Large content viewer support in UIKit import UIKit class FigureCell: UIStackView { var systemImageName: String! var imageTitle: String! var imageLabel: UILabel! var titleImageView: UIImageView! required init(coder: NSCoder) { super.init(coder: coder) setupFigureCell() } init(systemImageName: String, imageTitle: String) { super.init(frame: .zero) self.systemImageName = systemImageName self.imageTitle = imageTitle setupFigureCell() self.addInteraction(UILargeContentViewerInteraction()) self.showsLargeContentViewer = true self.largeContentImage = UIImage(systemName: systemImageName) self.scalesLargeContentImage = true self.largeContentTitle = imageTitle } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。