【SwiftUI】Viewに検索バーを追加する【.searchable】【Xcode13&iOS15】

Xcode13 及び iOS15 から NavigationView に検索バーを付けることができるようになりました。

本記事では簡単な説明とサンプルコードを紹介します。

【SwiftUI】Viewに検索バーを追加する【.searchable】【Xcode13&iOS15】

使い方

Viewプロトコルに新たに .searchable というプロパティが追加されました。これを NavigationView に定義することで簡単に検索バーを表示することができます。

引数

  • text: Binding<String>
  • placement: SearchFieldPlacement
  • prompt: Text?

text は検索バーに入力している文字列を受け取る @State or @Published な String型を渡します。

placement は検索バーをどの位置に表示するか指定します。以下の4が指定できます。

  • .navigationBarDrawer:今回のサンプルではこちらを使用しています。
  • .sidebar
  • .toolbar
  • .automatic

prompt は未入力時の仮の文字列(いわゆるプレースホルダー)を指定します。

サンプルコード

if #available(iOS 15.0, *) {} で括っていますので iOS15未満の環境では動作しませんので注意して下さい

内容は固定の配列から英語圏のファーストネームを List と連携して検索表示するものです。

検索バーで文字列を入力を決定(Returnキー押下)した時に抽出する処理を行っています。

検索文字列の決定をキャッチするには .onSubmit(of: .search) を定義する必要があります。

struct ContentView: View {
    
    @ObservedObject var model: Model
    
    var body: some View {
        if #available(iOS 15.0, *) {
            NavigationView {
                List {
                    Section {
                        ForEach(model.familyNames, id: \.self) { familyName in
                            Text(familyName)
                        }
                    }
                }
                .navigationBarTitle(Text("Family Names"))
                .onAppear() {
                    model.fetchFamilyNames()
                }
            }
            .searchable(
                text: $model.searchText,
                placement: .navigationBarDrawer(displayMode: .always),
                prompt: Text("all")
            )
            .onSubmit(of: .search) {
                model.fetchFamilyNames()
            }
        } else {
        }
    }
}

class Model: ObservableObject {
    
    @Published var searchText = ""
    @Published var familyNames: [String] = []
    
    func fetchFamilyNames() {
        if searchText.isEmpty {
            familyNames = sampleNames
        } else {
            familyNames = sampleNames.filter {
                $0.contains(searchText)
            }
        }
    }
    
    private let sampleNames: [String] = [
        "Lewis","Burton","Hamilton","Hampshire","Sutton","Scott","Kelly","Hill",
        "Green","Cox","Smith","Taylor","Clark","Walker","Wright","Baker",
        "Carter","Turner","Parker","Stewart","Cook","Miller","Jones","Davis",
        "Wilson","Anderson","Jackson","Thompson","Robinson","Watson","Peterson","Sullivan",
        "Russell","Gray","Long","Young","Bell","Campbell","Johnson","White",
        "Cavendish","Spencer","Gruber","Murphy",
    ]
}

なお、自動的に Cancel ボタンやクリアボタン(✖️ボタン)が付いていますがこれらのイベントをキャッチする方法がわかっていません

ご存知の方がいらっしゃいましたらコメント欄にお願いできましたら幸いです

以上

2件のコメント

毎回興味深く読ませていただいています。
さて.searchableの件ですがこちらのソースをベースに調べてみました。
私の調査ではアプリには[X][Cancel]押下は伝わらないように思えます。
自動で「.searchable(text:」が書き換わるだけ、のような気がします。
以下、ご参考。
struct ContentView: View {
@ObservedObject var model: Model
var body: some View {
NavigationView {
List(model.familyNames, id: \.self) { familyName in
Text(familyName)
}
}
.searchable(text: $model.searchText,
placement: .navigationBarDrawer(displayMode: .always))
}
}
class Model: ObservableObject {
@Published var familyNames: [String]
var searchText = “” {
didSet {
familyNames = self.sampleNames.filter { searchText.isEmpty || $0.contains(searchText) }
}
}
init() {
familyNames = sampleNames
}

ima. さん
コメントありがとうございます。

>私の調査ではアプリには[X][Cancel]押下は伝わらないように思えます。
そのようですね。
そのうち改良されるかもしれませんが残念。

コードもありがとうございます!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です