这篇文章介绍了SwiftUI的searchable()修饰符,iOS 16引入的功能允许添加搜索栏,并在iOS 17中作了改进,如支持NavigationStack、监控搜索活动状态及自定义搜索范围,增强搜索体验。
在iOS 16的更新中,SwiftUI为开发者们带来了一个新的功能——searchable()修饰符。这个修饰符允许我们直接在NavigationView中放置一个搜索栏。在简单的布局中,这个搜索栏保持固定位置;而当与列表一起使用时,它会自动出现并随着列表的滚动而滚动。
最简单的使用方法是在导航视图内的某个视图上添加searchable(),就像这样:
Text 1 2 3 4 5 6 7 8 9 10 11 struct ContentView: View {     @State private var searchText = ""     var body: some View {         NavigationView {             Text("Searching for \(searchText)")                 .searchable(text: $searchText)                 .navigationTitle("Searchable Example")         }     } } 
此外,你还可以为搜索框提供一个提示字符串,如下所示:
Text 1 2 3 4 5 6 7 8 9 10 11 struct ContentView: View {     @State private var searchText = ""     var body: some View {         NavigationView {             Text("Searching for \(searchText)")                 .searchable(text: $searchText, prompt: "Look for something")                 .navigationTitle("Searchable Example")         }     } } 
然而,在实际应用中,你更可能使用它来过滤一个数据列表,例如这样:
Text 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 struct ContentView: View {     let names = ["Holly", "Josh", "Rhonda", "Ted"]     @State private var searchText = ""     var body: some View {         NavigationView {             List {                 ForEach(searchResults, id: \.self) { name in                     NavigationLink(destination: Text(name)) {                         Text(name)                     }                 }             }             .searchable(text: $searchText)             .navigationTitle("Contacts")         }     }     var searchResults: [String] {         if searchText.isEmpty {             return names         } else {             return names.filter { $0.contains(searchText) }         }     } } 
当搜索栏出现在列表中时,它通常是隐藏的——用户需要轻轻向下拉列表的顶部才能显示出来。
对于更高级的用法,searchable()允许我们向用户显示一系列建议,甚至添加额外的完成信息以减少他们的输入量。这是通过向searchable()传递一个返回包含建议的视图的函数来实现的。如果你希望用户能够点击完成他们的搜索,请为每个建议使用searchCompletion()修饰符。
因此,我们可以修改前面的示例,提供用户输入时可点击的建议,而不是仅在原地过滤整个列表:
Text 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 struct ContentView: View {     let names = ["Holly", "Josh", "Rhonda", "Ted"]     @State private var searchText = ""     var body: some View {         NavigationView {             List {                 ForEach(searchResults, id: \.self) { name in                     NavigationLink(destination: Text(name)) {                         Text(name)                     }                 }             }             .searchable(text: $searchText) {                 ForEach(searchResults, id: \.self) { result in                     Text("Are you looking for \(result)?").searchCompletion(result)                 }             }             .navigationTitle("Contacts")         }     }     var searchResults: [String] {         if searchText.isEmpty {             return names         } else {             return names.filter { $0.contains(searchText) }         }     } } 
这样,“Are you looking for Holly?”和类似的建议就可以在屏幕上显示出来。同时,每个人的名字都被用作完成提示,这意味着如果你输入“Ho”并点击“Holly”,搜索栏将自动完成为全名。
对于更高级的搜索,你可以为搜索框添加搜索范围 ,让用户选择他们想要的搜索类型。例如,我们可以编写一些代码,让用户在搜索他们的收件箱或仅搜索他们的收藏消息之间进行选择,如下所示:
Text 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 struct Message: Identifiable, Codable {     let id: Int     var user: String     var text: String } enum SearchScope: String, CaseIterable {     case inbox, favorites } struct ContentView: View {     @State private var messages = [Message]()     @State private var searchText = ""     @State private var searchScope = SearchScope.inbox     var body: some View {         NavigationView {             List {                 ForEach(filteredMessages) { message in                     VStack(alignment: .leading) {                         Text(message.user)                             .font(.headline)                         Text(message.text)                     }                 }             }             .searchable(text: $searchText, scope: $searchScope) {                 ForEach(SearchScope.allCases, id: \.self) { scope in                     Text(scope.rawValue.capitalized)                 }             }             .navigationTitle("Messages")         }         .onSubmit(of: .search, runSearch)         .onChange(of: searchScope) { _ in runSearch() }     }     var filteredMessages: [Message] {         if searchText.isEmpty {             return messages         } else {             return messages.filter { $0.text.localizedCaseInsensitiveContains(searchText) }         }     }     func runSearch() {         Task {             guard let url = URL(string: "https://hws.dev/\(searchScope.rawValue).json") else { return }             let (data, _) = try await URLSession.shared.data(from: url)             messages = try JSONDecoder().decode([Message].self, from: data)         }     } } 
以上代码展示了如何在SwiftUI中使用searchable()修饰符来创建更动态和交互性更强的搜索体验。通过添加搜索范围和过滤条件,开发者可以提供更精准和个性化的搜索功能,从而提升用户体验。这些功能的引入,无疑使SwiftUI成为一个更加强大和灵活的UI框架,进一步推动了其在现代iOS应用开发中的应用。
iOS 17版本在searchable有一些变化,在讨论iOS 17中SwiftUI的searchable()修饰符的更新时,我们可以注意到几个关键区别与新增功能,相比于iOS 16版本。下面我将逐一解释这些变化,并为每个变化提供一个示例:
引入NavigationStack的支持 :
iOS 16 :searchable()修饰符主要用于NavigationView。iOS 17 :searchable()现在支持用于NavigationStack。示例 :在iOS 17中,你可以将searchable()直接应用于NavigationStack中的视图。 
1 2 3 4 5 6 7 8 9 10 struct  ContentView : View      @State  private  var  searchText =  ""      var  body: some  View  {         NavigationStack  {             Text ("Searching for \(searchText) " )                 .searchable(text: $searchText )         }     } } 
监控搜索活动状态 :
iOS 17  新增:可以绑定一个布尔值来监控搜索栏是否正在显示。示例 :在iOS 17中,使用isPresented绑定来监控搜索栏的显示状态。 
1 2 3 4 5 6 7 8 9 10 11 struct  ContentView : View      @State  private  var  searchText =  ""      @State  private  var  searchIsActive =  false      var  body: some  View  {         NavigationStack  {             Text ("Searching for \(searchText) " )             .searchable(text: $searchText , isPresented: $searchIsActive )         }     } } 
搜索范围的定制化 :
iOS 17  新增:通过searchScopes()修饰符来控制搜索的范围。示例 :在iOS 17中,使用searchScopes()允许用户选择搜索的特定范围。 
1 2 3 4 5 6 7 8 9 10 11 12 struct  ContentView : View      @State  private  var  searchText =  ""      @State  private  var  searchScope =  SearchScope .inbox     var  body: some  View  {         NavigationStack  {             List  {  }             .searchable(text: $searchText )             .searchScopes($searchScope ) {  }         }     } } 
 
这些改变展示了SwiftUI的逐步进化,以及苹果如何不断增强和完善其框架来提供更灵活、更强大的开发工具。通过这些更新,开发者能够为用户提供更丰富和便利的搜索体验。
参考网站 How to add a search bar to filter your data 
张洪Heo
分享设计与科技生活
本文是转载或翻译文章,版权归原作者所有。建议访问原文,转载本文请联系原作者。