在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版本。下面我将逐一解释这些变化,并为每个变化提供一个示例:

  1. 引入NavigationStack的支持

    • iOS 16searchable()修饰符主要用于NavigationView
    • iOS 17searchable()现在支持用于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)
    }
    }
    }
  2. 监控搜索活动状态

    • 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)
    }
    }
    }
  3. 搜索范围的定制化

    • 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