ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
SwiftUIで創り上げる検索体験
Appの中で特定のコンテンツをすばやく見つけられるようにする方法を紹介します。SwiftUIの修飾子.searchableを他のビューと組み合わせて使用し、Appに検索を組み込む方法を確認します。そして、実行可能な検索タイプをユーザーが理解するのに役立つ検索提案を提供することで、実装を向上させる方法をお伝えします。
リソース
関連ビデオ
WWDC21
-
ダウンロード
♪ ♪ こんにちは SwiftUIチームの エンジニアのHarryです 「SwiftUIで創り上げる検索体験」 にようこそ 今年 SwiftUIの検索を一新しました これらの機能の使用開始方法を ご紹介します ときに Appの中で特定のものを 探したいときもありますが Appの多くは 大量のデータを含んでおり 探しているAppを 正確に見つけるのは難しいものです 最新のAppのトレンドや 次のお気に入り曲 朝の通勤に最適な ポッドキャストなどは 検索を実装することで 見つけられるようになります 最適な検索体験は Appの構造や内容によって異なります しかし 一般的に全ての体験には 検索クエリを定義する 検索フィールドが含まれます SwiftUIでは直接 検索が 実装できるようになりました ご覧ください ここでは SwiftUIの 検索機能の基盤となる 新しい searchable修飾子を 紹介します ナビゲーションビューがどうやって searchable修飾子と 統合するか紹介します 最後に検索候補を追加することで searchable修飾子の機能が 拡張することを説明します searchable は SwiftUI の 新しいビュー修飾子で 全てのプラットフォームで 利用できます 表示されたコンテンツが 検索可能だと知らせることができます コンテンツがその意味を 明確にしてくれるでしょう この修飾子の各プラットフォームでの 使用例を紹介していきますね さて この修飾子を理解するために 天気について話しましょう! 新しいWeather Appで iOS検索の良い例が見つかるでしょう 都市のリストを見ながら 検索バーに文字を打ち始めると リストに追加する新しい 都市が検索されます Weatherがこの機能を 実行するために どのように searchable修飾子を 使用するのか UIの構造を 分類して説明します Weatherは ナビゲーションビューから始まり ナビゲーションバーを提示します Weatherはナビゲーションビューの コンテンツとして カスタムリストを追加し リスト内のセルに ForEachを追加します 最後にWeatherは ナビゲーションビューに searchable修飾子を加えます 全てのsearchable修飾子の 核となるのは 検索フィールドの設定です searchable修飾子は 設定された検索フィールドを 環境を通して 各プラットフォームに最適な方法で 使用できるように 他のビューに渡します ナビゲーションビューは 検索フィールドを理解し 検索バーとしてレンダリングします 設定された検索フィールドを 使用するビューがない場合 searchable修飾子は 検索 フィールドをレンダリング する デフォルトの実装を ツールバーに表示します 検索フィールドは必ずしも 唯一の検索関連の UIというわけではありません 検索結果を何らかの形で表示するのが 一般的なデザインパターンです Weatherはこのパターンに従い 空でない検索クエリを見るたび カスタムリストを 最新のクエリの結果を含む 別のリストに切り替えて表示します WeatherがUIを実現するため searchable修飾子によって 提供される情報をどのように 利用するか見てみましょう これはweatherのカスタムリストです searchable修飾子は isSearchingという新しい 環境プロパティを設定します Weatherはそれを ユーザーの積極的な検索が あるかどうかに基づき 表示されたビューを動的に 変更するために使用します WeatherはisSearching 環境プロパティと 検索テキストをクエリして 条件付きで結果ビューを表示します 独自の結果をレンダリングする場合 ユーザーが検索から戻った後 メインUIの状態が変化しないように オーバーレイの使用を 検討してください ナビゲーションビューと 新しいsearchable修飾子が どのように統合されるか 詳しく説明します そのために 私が精力的に 取り組んでいるApp Colorsを紹介します このAppではパレットと呼ばれる 好きな色のセットを ライブラリーで管理できます 例えば ルームや Appのテーマを 違う色にいじってみたり 新しいM1 iMacの色選びに役立ちます どんな望みでも Colorsは あなたをサポートします Colorsの基本構造はダブルカラムの ナビゲーションビューで ルートビューは iPadOSとmacOSではサイドバー 他のプラットフォーム上では ナビゲーションスタックの ルートになります サイドバーが私のカラー パレットのライブラリです 詳細表示では 現在選択されているカラーパレットと どのように見えるか 見え方の違いを 確かめることができます Appの使用が始まって気づいたことは みんなカラーが大好きだと いうことです ライブラリーには常に カラーが増え続けます しかしライブラリーが 大きくなるにつれて 全てスクロールしないと 特定のカラーパレットを 見つけるのが難しくなります この時点で 検索機能を 追加することに決定しました 私のAppユーザーが すぐに理解できるように 機能は プラットフォームの 慣習に従ったものにします この機能を実装するために いかにsearchable修飾子を 使用するか ご覧ください
これはColor Appの一部として 私が構築した ナビゲーションビューです 検索を実装するために ナビゲーションビューに searchable修飾子を加えます 先ほどと同様 検索クエリを サポートする階層に searchable修飾子を 縛りを設けて加えます そして iOSやiPadOS上では 検索バーとしてレンダリングされます ナビゲーションビューが searchable修飾子の コンテンツである場合 検索フィールドを カラムの1つに紐付けます どのカラムを使用するかは ナビゲーションビューに 提供されているカラム数によります 2カラムの ナビゲーションビューの場合 iOSとiPadOS上のどちらも 検索バーはサイドバーの カラムに紐付けられます 検索フィールドを デフォルト以外のカラムに 紐づけたいときは 既存のtoolbar修飾子と 同じように searchable修飾子を 目的のカラムに配置できます このAppではナビゲーションビューで 検索可能にしておきます Weather Appと同様に isSearching環境プロパティを使って 検索結果をサイドバーに 動的に表示させます macOSでsearchable修飾子を 同様に配置すると ツールバーの後続スペースに 検索フィールドが配置され ウィンドウが縮小すると 自動的に折り畳まれるなど プラットフォームで期待 される動作が行われます ここで macOSの 典型的な体験として 検索結果をAppの 詳細ペインに表示してみます WatchOSもiOSと似ており ツールバーのビューの上部に 検索フィールドを配置します ここで SwiftUIは 検索フィールドを紐付ける 最初のカラムを選択します searchable修飾子の配置を変えずに プラットフォーム間で異なる 動作をさせたことに注目です プラットフォーム間で 私のApp構造は変わらず ナビゲーションビューは ダブルカラムのままでした SwiftUIはこの構造や 異なるプラットフォームの 慣習を理解し 代わりに対処してくれます tvOSを見たとき 他のプラットフォーム上で App構造はより適切に なり得ると気づきます tvOSでは 検索はタブビュー 内のタブになりますが 私のAppにはタブビューがありません Appに少し手を加えると解決できます ダブルカラムの ナビゲーションビューでなく タブビューをコンテンツ とするシングルカラムの ナビゲーションビューを レンダリングすることで より典型的なtvOS体験 が可能になります タブビューの中に 既存の サイドバービューを配置し 新しい検索タブを追加します 検索タブはプレースホルダービューを 示しており 誰かが最初に そのタブに誘導したときに 見えるようになります 最後に ナビゲーション ビューをラップする代わりに searchable修飾子を移動して 検索タブをラップします 空でない検索クエリが入力されたら 検索結果を表示するように ビューを移行します これで各プラットフォームに 検索機能が加わりました SwiftUIの宣言的な性質のおかげで Appのナビゲーション構造が 一貫したままなので SwiftUIに頼ることができました そして特定のナビゲーション構造に 適したインターフェースを searchable修飾子の実装に 選ばせています tvOSでは Appの構造が変化したので seearchable修飾子について 学んだことを その変化した構造に適用しました 私が ”searchable”と 定義したものだけが 構造とともに変化しました これであなたは searchable修飾子について 理解しましたね 最後のトピックである 検索候補に移りましょう 私のAppで検索を使用した ユーザーから 検索の新機能は気に入っているが 何を検索していいのか わからないことがあると 報告を受けました 多くのAppが検索候補を使って 提供できる検索クエリの 種類を案内しています この候補は 完全な 検索クエリを示しており macOSのように メニューで表示されたり iOSのようにリストで表示されたり watchOSのようにリストを 表示するボタンになります 候補は どんなものを 検索すればよいのか 発想を与えてくれます SwiftUIはAppに検索候補を加える 簡単な方法を提供します ご覧ください 私のAppではsearchable修飾子を テキストで設定しています searchable修飾子は 候補と呼ばれるオプションの パラメータを提供しており スタート時に含められます 候補パラメータには ビューを提供します これは静的なボタンのときも ありますが Appのデータベースや サーバーから送られる動的な 候補のセットに対する ForEachがふさわしいです SwiftUIはこのビューを見て 表示すべき候補の有無に 基づいて提示します 例えば watchOSでは 空でない候補を入力すると 検索フィールドに アイコンが表示されます 一般的なパターンでは ForEachボタンを提示し 操作されるとsearchable修飾子に 提供されている テキストバインディングを 検索候補のテキスト値に更新します このパターンが通常になると 予測されるため 追加したのが searchCompletion修飾子です searchCompletion修飾子は 非対話型ビューで使用できます そのビューをボタンに変換し 検索テキストを更新して 現在表示されている候補を解除します ユーザーが初めて候補と 交流するAppを 作成するならば 候補に基づいた検索結果の 完全なセットをフェッチし いつ検索結果を フェッチしたかわかるよう 新しいonSubmit修飾子の 利用を検討してください onSubmit修飾子が検索の値を入れると ユーザーが検索クエリを送信するたび 提供したクロージャーが 呼び出されます 通常はユーザーが 検索候補を選択したり キーボードでEnterを 押したときに起こります また新しいonSubmit修飾子は テキストフィールドや セキュリティフィールドなど 検索に関係のない分野でも 使用できます 検索完了修飾子と一緒に 候補のパラメータを使うと 強力な検索候補機能を Appに簡単に追加できます SwiftUIが提供する 検索機能の一部を 簡単に紹介しましたがいかがでしたか searchable修飾子によって ビューコンテンツを 検索可能なものとして記述できます ナビゲーションビューは searchable修飾子と統合し ナビゲーションビューの コンテンツに基づき プラットフォームに適した 体験を提供します 環境のisSearching プロパティを使用すると ユーザーが検索しているときに AppのUIを動的に調整できます 検索完了修飾子と earchable修飾子の候補の パラメータを使用して Appに検索候補を追加します さああなたのSwiftUI Appに 検索を追加してみましょう それでは素敵なWWDCを [音楽]
-
-
0:10 - Colors Suggestions
struct ColorsContentView: View { @State var text = "" var body: some View { NavigationView { Sidebar() DetailView() } .searchable(text: $text) { ForEach(suggestions) { suggestion in Button { text = suggestion.text } label: { ColorsSuggestionLabel(suggestion) } } } } }
-
1:17 - New Searchable Modifier
ContentView() .searchable(text: $text)
-
1:58 - Weather Search
NavigationView { WeatherList(text: $text) { ForEach(data) { item in WeatherCell(item) } } } .searchable(text: $text)
-
3:11 - Weather List
struct WeatherList: View { @Binding var text: String @Environment(\.isSearching) private var isSearching: Bool var body: some View { WeatherCitiesList() .overlay { if isSearching && !text.isEmpty { WeatherSearchResults() } } } }
-
5:07 - Colors Search
struct ColorsContentView: View { @State var text = "" var body: some View { NavigationView { Sidebar() DetailView() } .searchable(text: $text) } }
-
7:15 - Colors Search with TV
struct ColorsContentView: View { @State var text = "" var body: some View { NavigationView { #if os(tvOS) TabView { Sidebar() ColorsSearch() .searchable(text: $text) } #else Sidebar() DetailView() #endif } #if !os(tvOS) .searchable(text: $text) #endif } }
-
9:09 - Colors Search Completions
struct ColorsContentView: View { @State var text = "" var body: some View { NavigationView { Sidebar() DetailView() } .searchable(text: $text) { ForEach(suggestions) { suggestion in ColorsSuggestionLabel(suggestion) .searchCompletion(suggestion.text) } } } }
-
10:21 - On Submit
ContentView() .searchable(text: $text) { MySearchSuggestions() } .onSubmit(of: .search) { fetchResults() }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。