SwiftUI:当用户停止在 TextField 中输入时,如何仅 运行 编码?
SwiftUI: How to only run code when the user stops typing in a TextField?
所以我正在尝试制作一个搜索栏,它不会 运行 显示结果的代码,直到用户停止输入 2 秒(也就是当用户停止输入时它应该重置一种计时器输入一个新字符)。我尝试使用 .onChange() 和 AsyncAfter DispatchQueue 但它不起作用(我想我明白为什么当前的实现不起作用,但我不确定我是否以正确的方式解决这个问题)...
struct SearchBarView: View {
@State var text: String = ""
@State var justUpdatedSuggestions: Bool = false
var body: some View {
ZStack {
TextField("Search", text: self.$text).onChange(of: self.text, perform: { newText in
appState.justUpdatedSuggestions = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
appState.justUpdatedSuggestions = false
})
if justUpdatedSuggestions == false {
//update suggestions
}
})
}
}
}
可能的方法是使用 Combine 框架中的 debounce
。要使用它,最好使用已发布的 属性 为搜索文本创建单独的视图模型。
这是一个演示。使用 Xcode 12.4 / iOS 14.4.
准备和测试
import Combine
class SearchBarViewModel: ObservableObject {
@Published var text: String = ""
}
struct SearchBarView: View {
@StateObject private var vm = SearchBarViewModel()
var body: some View {
ZStack {
TextField("Search", text: $vm.text)
.onReceive(
vm.$text
.debounce(for: .seconds(2), scheduler: DispatchQueue.main)
) {
guard ![=10=].isEmpty else { return }
print(">> searching for: \([=10=])")
}
}
}
}
处理延迟搜索查询调用时通常使用两种最常用的技术:节流或去抖动。
要在 SwiftUI 中实现这些概念,您可以使用 Combine 框架 throttle/debounce 方法。
一个这样的例子:
import SwiftUI
import Combine
final class ViewModel: ObservableObject {
private var disposeBag = Set<AnyCancellable>()
@Published var text: String = ""
init() {
self.debounceTextChanges()
}
private func debounceTextChanges() {
$text
// 2 second debounce
.debounce(for: 2, scheduler: RunLoop.main)
// Called after 2 seconds when text stops updating (stoped typing)
.sink {
print("new text value: \([=10=])")
}
.store(in: &disposeBag)
}
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
TextField("Search", text: $viewModel.text)
}
}
您可以在官方文档中阅读有关 Combine 和 throttle/debounce 的更多信息:throttle, debounce
所以我正在尝试制作一个搜索栏,它不会 运行 显示结果的代码,直到用户停止输入 2 秒(也就是当用户停止输入时它应该重置一种计时器输入一个新字符)。我尝试使用 .onChange() 和 AsyncAfter DispatchQueue 但它不起作用(我想我明白为什么当前的实现不起作用,但我不确定我是否以正确的方式解决这个问题)...
struct SearchBarView: View {
@State var text: String = ""
@State var justUpdatedSuggestions: Bool = false
var body: some View {
ZStack {
TextField("Search", text: self.$text).onChange(of: self.text, perform: { newText in
appState.justUpdatedSuggestions = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
appState.justUpdatedSuggestions = false
})
if justUpdatedSuggestions == false {
//update suggestions
}
})
}
}
}
可能的方法是使用 Combine 框架中的 debounce
。要使用它,最好使用已发布的 属性 为搜索文本创建单独的视图模型。
这是一个演示。使用 Xcode 12.4 / iOS 14.4.
准备和测试import Combine
class SearchBarViewModel: ObservableObject {
@Published var text: String = ""
}
struct SearchBarView: View {
@StateObject private var vm = SearchBarViewModel()
var body: some View {
ZStack {
TextField("Search", text: $vm.text)
.onReceive(
vm.$text
.debounce(for: .seconds(2), scheduler: DispatchQueue.main)
) {
guard ![=10=].isEmpty else { return }
print(">> searching for: \([=10=])")
}
}
}
}
处理延迟搜索查询调用时通常使用两种最常用的技术:节流或去抖动。
要在 SwiftUI 中实现这些概念,您可以使用 Combine 框架 throttle/debounce 方法。
一个这样的例子:
import SwiftUI
import Combine
final class ViewModel: ObservableObject {
private var disposeBag = Set<AnyCancellable>()
@Published var text: String = ""
init() {
self.debounceTextChanges()
}
private func debounceTextChanges() {
$text
// 2 second debounce
.debounce(for: 2, scheduler: RunLoop.main)
// Called after 2 seconds when text stops updating (stoped typing)
.sink {
print("new text value: \([=10=])")
}
.store(in: &disposeBag)
}
}
struct ContentView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
TextField("Search", text: $viewModel.text)
}
}
您可以在官方文档中阅读有关 Combine 和 throttle/debounce 的更多信息:throttle, debounce