9.5 管理搜索界面的激活
原文链接: https://developer.apple.com/documentation/swiftui/managing-search-interface-activation
9.5 管理搜索界面的激活
以编程方式检测并关闭搜索框。
概述
人们通过轻点或点击来激活应用中的搜索框,之后就可以输入搜索词。在许多情况下,你的应用只需要对搜索文本值的相应变化做出反应——界面会通过你提供的绑定来更新这些值,如执行搜索操作所述。
不过,SwiftUI 也提供了一些控件,让你能以编程方式管理搜索界面。具体来说,你可以:
- 用你提供给 searchable 修饰符的绑定来激活或取消激活该界面。
- 通过读取环境值来检测该界面是否处于激活状态。
- 通过调用存储在环境中的动作来关闭该界面。
- 等到发生搜索提交事件后再开始搜索。
通过绑定控制激活
你可以给 searchable 修饰符的 isPresented 参数提供一个指向布尔值的 Binding,以编程方式控制搜索界面的激活。例如,要呈现一个出现时搜索界面就已激活的表单,可以创建一个初始值为 true 的绑定:
1
2
3
4
5
6
7
8
9
10
11
| struct SheetView: View {
@State private var isPresented = true
@State private var text = ""
var body: some View {
NavigationStack {
SheetContent()
.searchable(text: $text, isPresented: $isPresented)
}
}
}
|
在 iOS 和 macOS 上,SwiftUI 在呈现搜索时会聚焦搜索框,在关闭搜索时会取消聚焦搜索框。搜索界面仍然呈现时,搜索框也可能失去焦点。例如,如果你的搜索界面包含一列文本输入框,有人可能把焦点移到其中一个文本输入框上,而没有关闭该界面。
检测搜索的激活
如果你需要知道搜索界面何时处于激活状态,可以用 Environment 属性包装器查询环境中的 isSearching 属性。下面的例子展示了一个视图,它根据该属性的状态更新所显示的文本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| struct SearchingExample: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
SearchedView()
.searchable(text: $searchText)
}
}
}
struct SearchedView: View {
@Environment(\.isSearching) private var isSearching
var body: some View {
Text(isSearching ? "Searching" : "Not searching")
}
}
|
当有人第一次轻点或点击搜索框时,isSearching 属性会变成 true。当他们取消搜索操作时,该属性会变成 false。如果你以编程方式关闭该界面(如下一节所述),它也会变成 false。
请务必在直接被或间接被某个 searchable(text:placement:prompt:) 视图修饰符包裹的视图内部读取该属性,就像上面例子中的 SearchedView 那样。如果你在该上下文之外读取它(例如把它放在 SearchingExample 视图中),就检测不到该属性值的任何变化。
关闭搜索界面
你可以用环境中的 dismissSearch 动作以编程方式取消激活该界面。例如,考虑一个视图,其中有一个用于呈现集合中第一个匹配项更多信息的 Button:
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
| struct ContentView: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
SearchedView(searchText: searchText)
.searchable(text: $searchText)
}
}
}
private struct SearchedView: View {
var searchText: String
let items = ["a", "b", "c"]
var filteredItems: [String] { items.filter { $0 == searchText.lowercased() } }
@State private var isPresented = false
@Environment(\.dismissSearch) private var dismissSearch
var body: some View {
if let item = filteredItems.first {
Button("Details about \(item)") {
isPresented = true
}
.sheet(isPresented: $isPresented) {
NavigationStack {
DetailView(item: item, dismissSearch: dismissSearch)
}
}
}
}
}
|
只有有人输入了能产生匹配的搜索文本之后,这个按钮才会出现。按钮的动作会显示一个表单,提供关于该项的更多信息,其中还有一个 Add 按钮,用于把该项加入已存储的条目列表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| private struct DetailView: View {
var item: String
var dismissSearch: DismissSearchAction
@Environment(\.dismiss) private var dismiss
var body: some View {
Text("Information about \(item).")
.toolbar {
Button("Add") {
// 在这里存储该项……
dismiss()
dismissSearch()
}
}
}
}
|
人们可以向下拖动表单来关闭它,这相当于取消操作,并让进行中的搜索交互保持原样。他们也可以轻点 Add 按钮来存储该项。由于此时人们很可能已经用完了详情视图和搜索交互,按钮的闭包使用 dismiss 属性关闭表单,并用 dismissSearch 属性重置搜索框。
与 isSearching 属性一样,请只在 searchable 视图修饰符的层级内部读取 dismissSearch。如果你在该上下文之外从环境中读取它,调用该动作不会有任何效果。上面的例子是在 SearchedView 中读取该动作,并把它传入表单,因为表单有它自己的环境。如果搜索界面并未处于激活状态,该动作同样不会有任何效果。
对搜索提交做出响应
要指定当有人提交搜索查询(按下 Return 键)时 SwiftUI 所调用的动作,请添加 onSubmit(of:_:) 修饰符:
1
2
3
4
5
| SearchedView()
.searchable(text: $searchText)
.onSubmit(of: .search) {
submitCurrentSearchQuery()
}
|
视应用结构的不同,你可以用不同方式利用搜索提交。例如,你可以借这个机会在搜索查询字符串中寻找可以转换为令牌的子串。反过来,对于非常慢的搜索操作(也许是因为它需要访问网络),你也可以等到提交事件发生后再执行搜索。