Swiftでリストや検索対象を絞り込むUIを作る際に便利なのが .searchScopes
という修飾子です
これは SwiftUI の List
や NavigationStack
などで使える検索UI向けの機能で、ユーザーが検索範囲を切り替えられる「スコープボタン(セグメント)」を簡単に追加できます。
この記事では .searchScopes
の基本的な意味や仕組み、使い方、活用シーン、注意点までをわかりやすく丁寧に解説します。
.searchScopes とは?
.searchScopes
は、SwiftUIの View
に対して、検索スコープを切り替えるUI(セグメントコントロール)を表示するための修飾子です。
例えば、「すべて」「タイトル」「メモ」など、検索対象を絞るための選択肢をユーザーに提示したいときに使われます。
iOSの「メール」アプリなどでも見られる、検索バーの下にあるスコープ切り替えのUIが、.searchScopes
を使えば自作アプリでも実現できます。
具体例①:検索スコープを定義してリストを絞り込む
まずは基本的な例から見てみましょう。
1 2 3 4 5 6 7 8 |
enum ItemScope: String, CaseIterable, Identifiable { case all = "すべて" case title = "タイトル" case memo = "メモ" var id: Self { self } } |
このように検索スコープ用の列挙型を用意し、検索対象を切り替えるUIを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 |
struct Item: Identifiable { let id = UUID() let title: String let memo: String } // ダミーデータ let items: [Item] = [ Item(title: "買い物リスト", memo: "牛乳とパンを買う"), Item(title: "会議メモ", memo: "来週の打ち合わせの準備"), Item(title: "読書リスト", memo: "SwiftUIの本を読む") ] |
更に、サンプルデータを格納するItem構造体を作り、ダミーデータを格納します。
下記コードを実行すると.searchScopes
を基本的な挙動を確認できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
struct ContentView: View { @State private var searchText = "" @State private var selectedScope: ItemScope = .all var body: some View { NavigationStack { List(filteredItems, id: \.id) { item in Text(item.title) } .searchable(text: $searchText) .searchScopes($selectedScope) { ForEach(ItemScope.allCases) { scope in Text(scope.rawValue).tag(scope) } } .navigationTitle("検索リスト") } } var filteredItems: [Item] { // フィルタリングロジック(簡略化) items.filter { item in switch selectedScope { case .all: return item.title.contains(searchText) || item.memo.contains(searchText) case .title: return item.title.contains(searchText) case .memo: return item.memo.contains(searchText) } } } } |
検索バーに検索をしたいテキストを入力すると、上記のようにItemScopeで指定していたタブが表示されて、それぞれの内容に応じた値を表示できます。
このように .searchScopes
を使えば、選択したスコープに応じて検索対象を切り替えるUI が簡単に実装できます。
.searchScopes の仕組みと動き
.searchScopes
の基本的な動作のポイントを押さえましょう。
- .searchScopes は .searchable とセットで使います(単独では意味を持ちません)
- バインディングした変数(上記の selectedScope)に現在のスコープが代入される
- ユーザーがスコープを選択すると、その値が自動的に変わる
- .tag() でスコープごとの識別子を関連付ける
選ばれたスコープの値を元に、List や ForEach などの中でフィルタリングを行うのが基本です。
以上の流れを守ることで、UI操作とデータ絞り込みが自然に同期します。
.searchScopesの活用シーン
.searchScopes
は「同じ検索語でも、どの属性に対して検索するか」を切り替えたいときに力を発揮します。
代表的なユースケースは次のとおりです。
- ToDoアプリで「すべて」「完了」「未完了」などの絞り込み
- メモアプリで「タイトル」「内容」ごとの検索対象を切り替え
- 電話帳・商品一覧・ファイル管理など、属性別の検索絞り込み
.searchScopes
を使うことは、ユーザーにとっては検索体験を向上させ、開発者にとってはフィルタロジックを分離しやすくなるというメリットがあります。
.searchScopes の注意点
導入はシンプルですが、いくつかの型要件や記述上の注意を満たさないと正しく動作しません。
事前に次の点を確認しましょう。
.searchScopes を使うには、スコープにバインドする変数が Equatable + Identifiable である必要があります。
そのため、列挙型を使う場合は CaseIterable と Identifiable の準拠を忘れずに書きましょう。
また、.tag()
の付け忘れはよくある落とし穴です。
タグがないと「どのスコープが選ばれたか」を示せず、バインディングが更新されません。
例えばこういったミスはうまく動作しません:
Text("タイトル") // .tag(...) が抜けている
正しくは:
Text("タイトル").tag(ItemScope.title)
以上をおさえておけば、.searchScopes
を安定して運用でき、検索UIの拡張や保守も容易になります。
まとめ
今回は .searchScopes
について詳しく紹介しました。
.searchScopes
は、検索バーにスコープ切り替えUIを追加できるSwiftUIの修飾子.searchable
と併用して、検索対象を動的に変更できる- 列挙型を使うとスコープの管理が簡潔に行える
- 検索体験の向上や属性別フィルタリングに役立つ
.tag()
やIdentifiable
の設定ミスに注意
検索機能に柔軟性を持たせたいとき、.searchScopes
は非常に有効です。
シンプルな書き方で使えるため、まずは小さなリストから試してみるのがおすすめです。