ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Formatters:データをヒューマンフレンドリーに
時間の節約とフラストレーションの軽減:Appに日付、時間、測定結果、名前、リスト、数字、文字列などのデータを表示する際に、それを正しくフォーマットして優れたエクスペリエンスを提供する方法を学びましょう。ここでは、Formatter APIと、stringsdictでSwiftUIが機能する仕組みをご紹介し、データのフォーマットに関する手間のかかる作業の実行にそれらがどのように役立つかをご覧いただきます。ベストプラクティスと、よくあるミスの回避方法を学びましょう。
リソース
- Displaying Human-Friendly Content
- Expanding Your App to New Markets
- Unicode Date Field Symbol Table
- Xcode Help: Add language plural variants
関連ビデオ
WWDC22
WWDC21
WWDC20
-
ダウンロード
こんにちは ようこそWWDCへ
“Formatters: データをヒューマンフレンドリーに” みなさん こんにちは カラン・ミシュラです 今日はアプリケーションのデータを 扱いやすく わかりやすくする方法をお話しします 毎日 さまざまな種類のデータを表示する たくさんのアプリケーションを使用しています たとえば天気アプリケーションは さまざまな計測でいっぱいです ほかにも ヘルスアプリケーションでは 傾向と主要な統計を表示します さらに“メモ”のように単純な日付とタイム スタンプだけしか表示しないものもあります データが どれほど目立つように 表示されていたとしてもも どの単位 どのフォーマットが使用されて いるのか 正しく理解することが重要です これは極めて重要なことで アプリケーションが 1つの言語のみで利用できるのか または 複数の異なる言語で 利用できるのかは関係ありません そして フォーマッタを強化する 基本的な アルゴリズムとデータセットをアップデートし― 言語と地域の組み合わせに 対するサポートを拡大しました なぜか? 我々はグローバル化がすすむ 世界で生活しているからです 世界中でユーザーが 地域と伝統的に関連のない言語で デバイスを使用しています
たとえば アブダビに住む人が フランス語でiPhoneを使うかもしれません “言語と地域のさらなる組み合わせ” 以前は ここでホームアプリケーションに 表示されているように フランス式の数値形式で 小数点区切りに コンマを使用することを意味していました
現在 iOS 14 最新バージョンのMac OS watchOS tvOSでは より地域に合う― この場合は UAEに適した形式になります そして 言うまでもなく この変更はひとつの言語と地域に限定されず 無数の言語と地域の組み合わせにわたる フォーマットを改善する 一般的なアルゴリズムです
アプリケーションでフォーマッタAPIを 使用するメリットと それにより節約できる時間と労力 そして APIを使用し 多用途のUIを構築する方法をご紹介します “すべてをフォーマットする” いくつかのセクションで説明します 各セクションを説明するさい Apple独自のアプリケーションの 実際の例を用い 最も具体的なものから 一般的なものの順に説明します 私の目標は 自由に使用できる 広範なAPIセットを紹介することです シンプルなことは簡単にでき 複雑なことも可能です “日付と時刻‐測定単位- 名前 - リスト - 数値 - 文字列” 日付と時刻からはじめましょう
シンプルな例を用います “メモ”ではメモの最上部で 最後に変更した 日付と時刻を確認できます なにができるか見ていきましょう
ここに年月日 そして時刻で 完全な日付を表示したいです これはDateFormatterの 事前に定義されたスタイルで達成でき 日付にはミディアムスタイルを 時間にはショートスタイルを使用します ミディアム長の形式であるため “at”という単語が自動的にここに 追加されることに注意してください お伝えしたとおり シンプルなことは簡単にできました
では より複雑なことも可能であることを ご紹介します 設定でスクリーンタイムのUIが 表示されています ここで日付 月につづき 曜日を表示したい とします でも 年はふくみません これは事前定義スタイルが 提供するものではなく テンプレートを指定することで 可能です
スクリーンタイムのもう一つの例で ほかの多くのUIでも見られることですが 曜日の省略形のみ必要な場合 カスタムテンプレートでリクエストできます ご覧のとおり テンプレートのメカニズムは本当に強力です ではテンプレートについて 詳しくみていきましょう “テンプレート” Unicodeはさまざまな 技術報告書を公開していて “日付”を含む7シリーズから成ります 実は AppleがUnicodeメンバーとして 主要な貢献者の1つとなっていることを 誇りに思っています これはテンプレートへ参照するのに優れていて 多くの説明と例が記載されています 曜日を探すときには スクロールして このセクションを見つけます ここの 大文字の“E”欄と 小文字の“e”と“c”欄の中から選べます どれを使うべきでしょう? 覚えておいでと思いますが グラフで曜日ラベルを表示していたので これらはスタンドアロンで ロング形式の一部ではありません これがテンプレートに 小文字の“c”を選択するのに役立ちます
テンプレートを選択したところで どう機能するかみてみましょう
さまざまな長さの小文字“c”の 出力が表示されています ここで5つのCを含むテンプレートが 適切だということに注意してください 最も省略された形式が必要なためです また これらの長さの解釈のされ方が さまざまな言語間で 異なることに気を付けてください 英語では5つの長さがすべて異なって 表示されていますが― アラビア語と日本語ではそうではありません なので 適切なテンプレートの選択が重要で サポートしているすべてのロケールを考慮します 一部の違いは 特定の言語または ロケールのみで現れる場合があるためです これがスタンドアロンスタイルを選択することが 極めて重要だった理由でもあります 英語では違いはありませんが 他の言語では生じるかもしれません テンプレートについてもう一つ大事なことは フィールドの順序は 重要ではないということです このテンプレートも これも このテンプレートも 同じです
テンプレートは私たちが興味のある データの一部を取り出し それをロケールにとって 意味あるものに構築するのは フォーマッタの仕事です
アメリカ英語では みてわかるように コンマを追加します イギリス英語では このように 日に月が続くようにします 逆ではいけません 最後の例ですが 日本語では“月”と“日”の文字を追加します 日本語で日付を分かりやすくするために必要です
さいごにDateFormatterは dateFormatというプロパティをもちます テンプレートをプロパティに 直接セットしてはいけません それが文字通りに解釈され ほぼ確実に間違った結果を生じさせるからです かわりに 常にテンプレートから 日付フォーマットを作成すべきです
日付と時刻をフォーマットするための APIがさらにいくつかあります
DateComponentsFormatterは 期間に使われているような コンポーネントのフォーマットに役立ちます また 時間範囲も このヘルスアプリケーションのように DateIntervalFormatterでフォーマットできます 開始日と終了日の両方にあるエレメントの 繰り返しの回避にも対処します
さいごに RelativeDateTimeFormatterは 強力なツールで 自然な方法で過去や未来の日付を表示します 次は 計測についてみていきましょう アプリケーションが 何かしらの計測を使用 する場合 MeasurementFormatterが有用です 天気アプリケーションでいくつか使用例を みてみましょう
温度からはじめます すべての例はメートル単位で 計測を設定していますが 右側のディスプレイの表示はすべて US向けにローカライズされ ヤード単位を使用しています ですので摂氏16度は 華氏61度で表示されます スピードも同じです 時速の値がUS向けに マイルになります 気圧も同様です これらの例をお見せするのは 計測のデータソースがメートル 又は その他の単位を使用するかもしれないからです MeasurementFormatterは 面倒な作業を 単位にとどまらず 言語とロケールにわたってすべて行います 3種類の異なる種類の計測を ご紹介しましたが MeasurementFormatterは さらに多くをサポートしています そして 使用したい単位をサポートしていない場合 カスタム単位の作成も可能です 詳細は WWDC 2016 の素晴らしい “Measurements and Units”をご覧ください
次に 名前について話します
名前はアプリケーションに表示する 最も個人的なデータの1つで― 誰しも画面で名前を見ると 非常に強く反応します 名前形式を正しく取得することは よい印象を与えるために きわめて重要です 幸い PersonNameComponentsFormatterの使用で― 略して“Name Formatter”と呼んでいます 正しいことを簡単に実行できます
やることは コンポーネントオブジェクトを設定し フォーマットされた文字列を 要求することだけです
デフォルトではName Formatterは ミディアムスタイルを選びます 姓と名などの すべての主要なコンポーネントが含まれます
ユーザーの好みに合わせてショートスタイルも 要求でき― ニックネームや名前の短縮版に使用します
ここで見られるように 省略形はモノグラムのような 特定のユースケースが対象です ここ数年でより多くのアプリケーションが モノグラムを使用するようになりました では より徹底的にみていきましょう
モノグラムは写真やアバターがない場合に 最適な選択肢で UIを親しみやすく魅力的なものにします Name Formatterはモノグラムを 考慮して設計されています ただし すべての名前に対し モノグラムを生成することはできず 一部の省略名は長すぎて 特定のUIに収まらない場合があることに 注意してください Swiftで長さチェックに基づいて モノグラムを制限するのは簡単です Swiftカウントはユーザー視認文字に 基づいていて Unicodeコードポイントではないので 言語間でうまく機能します もちろん文字数はレンダリング時の 文字列の高さと幅を決定することはできません なので文字列が適切に 収まるか確認する必要があります
文字数が長すぎる または 文字列が大きすぎて収まらないとき フォールバックオプションを 使用する必要があります モノグラムの代わりに使われる 一般的なアイコンのようなものです
さいごに お話ししたいのは Name Formatterには 多くのインテリジェンスが組み込まれていて 名前をフォーマットするさいに いくつかの情報を使用するということです ユーザーの言語設定 名前の順序などの連絡先設定 名前そのものなどです
たとえば ここでは英語で動作しているiPhoneに 日本人の名前が表示されています 意図的にこの例をお見せしています Name Formatterは すべてのケースで適切に 名前をレンダリングします 名前の言語がiPhoneの言語と 異なる場合でもそうです
中国語 日本語 韓国語で書かれた名前の場合 常に姓のあとに名が続きます それが名前を書くための唯一の適切な方法です 次に 適切にフォーマットされた リストの作成方法を見てみましょう 人間が読めるテキストとしてフォーマットされた アイテムのリストを表示するさいには ListFormatterを使用できます 時間を節約するだけでなく 微妙な差異を正しくすることで アプリケーションの 見た目を洗練することを紹介します APIはこれほど簡単ではありません シンプルに文字の配列や そのほか リストに組み合わせたい オブジェクトを特定し― 結合リストを返します これにより得られるメリットを見ていきましょう UIのキーボード設定です 2つのとてもシンプルなリストで フォーマットしています 言語名は“英語”と“スペイン語”です 英語では途中に “and”という単語を挿入するだけです 言語の順序が変更された場合も 同じです しかし スペイン語にローカライズされた 同じUIをみてみると 単語“and”がスペイン語で表示されます ただ スペイン語の“and”はかなり 文脈によって変化することをご存知でしょうか? エレメントの順序を変更するさい スペイン語での“and”も 変化するということに注意してください 実際 iOS 14では 最新バージョンのmacOS tvOS watchOSと同様に ListFormatterは文法のルールを 順守するよう更新されました スペイン語を含む いくつかの言語が対象です これが フォーマッタAPIを できる限り使用する 多くの理由のうちの1つです 膨大な複雑性を抽象化し ローカリゼーションを無料で提供するだけでなく 基盤となる実装が更新と改善されるため コードを一行も修正することなく アプリケーションの動作を改善します ここで二つの一般的な データタイプについて話します 数字から始めましょう クイズです 画面になんの数字が見えていますか? ちょっとしたひっかけ問題です どの言語を使用し どの地域にいるかによって これを32,768か32.768として 解釈します この場合 電卓には小数点がなく 32,768と表示しています
フォーマットする方法をご説明します とても簡単です
アプリケーションがアラビア語でエジプト地域で 立ち上げられた場合どうなるでしょう? 見てわかるとおりNumberFormatterが 面倒なことを行ってくれます すべてを適切に確実にローカライズします
言語やロケールにより変化する 特定の記号にアクセスする必要がある場合 パーセント記号と小数区切り記号を引き出す 便利な方法があります 実際には もっと多くの便利なAPIがありますが ここには示されていません
NumberFormatterは いくつか異なるスタイルをサポートし― パーセントスタイルも含みます ここでも同じように 言語と地域における微妙な差異を 処理することを期待します たとえば これが数字を適切に フォーマットしているのが分かります トルコ語向けにパーセント記号が 数字の前にきています ここではこれ以上 NumberFormatterの詳細をお話しません 役立つ機能が 圧倒的に多いためです ですが ドキュメントをチェックするか Xcode Playgroundで使用してみてください さいごに文字列についてのお話です さまざまなフォーマッタAPIが すべてのアプリケーションで 共通してフォーマットされている 広範なデータタイプをカバーする一方で 標準フォーマッタを持たない独自のデータを アプリケーションが保持しているかもしれません たとえば写真の数をフォーマットする 標準フォーマッタはありません どう機能しているか例を見てみます 極めて単純にこれを行います SwiftUIでテキストエレメントを使用し 通常行うようにテキストを特定します コードが進む限り 私たちは書くだけです “STRINGSDICT” そして 対応する文字列をふくむ stringsdictファイルがあることを 確認するだけです stringsdictや一般的な ローカリゼーションについて詳細を知るには ここに示されているURLの ドキュメントを確認してください テキストを英語とアラビア語に 翻訳しました 結果がどのように見えるかみてみましょう 1枚の写真が選択されると英語では 数字の“1”が使用される一方で アラビア語ではより自然に表示するために 数詞に書き換えます
2枚の写真が選択されるさいは 英語ではここでも数字を使いますが 英文法に従い “photo”が“photos”となります 一方でアラビア語では アラビア語文法に準じて 双数形になります
3枚の写真だと 英語とアラビア語は似ていて “photo”の複数形に数字が続いています
10枚までは同じです 11枚以上になると アラビア語文法では異なる複数形を 使用することが規定されています みなさん いま いろんな言語の文法規則の 学習に興味を持ったかもしれませんね わかりますよ でもこのセッションでは つぎのことを理解していただきたいです stringsdictを使用して 文字列を正しくローカライズし― アプリケーションでできる限り フォーマッタを使用すると サポートする言語数に関係なく アプリケーションに磨きがかかります そしてこのすべてのことは たった一度コードを書くだけでできます 現在または将来的に アプリケーションで サポートする言語数は関係ありません
さいごに お話ししたフォーマッタのさらなる 機能をお見せし 探求してもらうために ダウンロードして試せるサンプル アプリケーションを用意しました フォーマットを楽しんでください
-
-
2:25 - Dates and times
// Dates and Times // Date with Day/Month/Year and Time let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium dateFormatter.timeStyle = .short dateFormatter.string(from: Date()) // Day of Week + Date + Month let dateFormatter = DateFormatter() dateFormatter.setLocalizedDateFormatFromTemplate ("MMMMdEEEE") dateFormatter.string(from: Date()) // Abbreviated Day of Week let dateFormatter = DateFormatter() dateFormatter.setLocalizedDateFormatFromTemplate ("ccccc") dateFormatter.string(from: Date())
-
5:56 - Date components formatter
// Dates and Times // Date and Time Components let formatter = DateComponentsFormatter() formatter.unitsStyle = .abbreviated let components = DateComponents(hour: 2, minute: 26) formatter.string(from: components) // Date and Time Intervals let formatter = DateIntervalFormatter() formatter.dateTemplate = "dMMM" formatter.string(from: startDate, to: endDate) // Relative Dates and Times let formatter = RelativeDateTimeFormatter() formatter.dateTimeStyle = .named formatter.localizedString(from: DateComponents(day: -1))
-
6:29 - Measurements
// Measurements // Temperature let formatter = MeasurementFormatter() let temperature = Measurement<UnitTemperature> (value: 16, unit: .celsius) formatter.numberFormatter.maximumFractionDigits = 0 formatter.string(from: temperature) // Speed let speed = Measurement<UnitSpeed> (value: 14, unit: .kilometersPerHour) formatter.string(from: speed) // Pressure let pressure = Measurement<UnitPressure> (value: 1.01885, unit: .bars) formatter.string(from: pressure)
-
7:49 - Names
// Names let formatter = PersonNameComponentsFormatter() var nameComponents = PersonNameComponents() nameComponents.familyName = "Iwasaki" nameComponents.givenName = "Akiya" nameComponents.nickname = "Aki-chan" // Full Name formatter.string(from: nameComponents) // Short Name: Respects User Preferences formatter.style = .short formatter.string(from: nameComponents) // Abbreviated Name formatter.style = .abbreviated formatter.string(from: nameComponents)
-
8:31 - Abbreviated name (monogram)
// Abbreviated Name: Monogram formatter.style = .abbreviated let monogram = formatter.string(from: nameComponents) if (monogram.count <= 2) { // Use Monogram } else { // Use Icon }
-
9:23 - Name formatter
// Names let formatter = PersonNameComponentsFormatter() var nameComponents = PersonNameComponents() nameComponents.familyName = "岩崎" nameComponents.givenName = "晃也" nameComponents.nickname = "あきちゃん" // Full Name formatter.string(from: nameComponents) // Short Name: Respects User Preferences formatter.style = .short formatter.string(from: nameComponents) // Abbreviated Name formatter.style = .abbreviated formatter.string(from: nameComponents)
-
10:15 - Lists
// Lists // English Localization let items = [ "English", "French", "Spanish" ] ListFormatter.localizedString(byJoining: items) let items = [ "English", "Spanish" ] ListFormatter.localizedString(byJoining: items) let items = [ "Spanish", "English" ] ListFormatter.localizedString(byJoining: items) // Spanish Localization let items = [ "Inglés", "Español" ] ListFormatter.localizedString(byJoining: items) let items = [ "Español", "Inglés" ] ListFormatter.localizedString(byJoining: items)
-
12:01 - Numbers
// Numbers let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.string(from: 32.768) // French (France) let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.string(from: 32.768) // Arabic (Egypt) formatter.percentSymbol formatter.decimalSeparator
-
12:33 - Numbers formatter
// Numbers let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.string(from: 0.71) // English (US) let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.string(from: 0.71) // Turkish (Turkey)
-
13:24 - Strings
// Strings var body: some View { Text("\(photosCount) Photos Selected") }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。