在 SwiftUI / Combine 中捕获绑定的最后更改
Capture the Very Last Change of a Binding in SwiftUI / Combine
当我在 SwiftUI 中有一个绑定并且我想在绑定更改时保存(例如在 TextField 上)
var myText: String { /* value is derived */ }
func save(_ text: String) { /* doing the save stuff */ }
TextFiel(text: .init(get: { myText }, set: { save([=10=]) })
这样做,只要绑定发生变化,就会调用 save()
。在某些情况下,这可能并不理想,例如当 save() 进行服务器调用或一些昂贵的计算时。所以我正在寻找的是,每当 binding 为 上次 更改时得到通知。
也许某种延迟的观察者在最终更改后触发 x 秒,如果另一个更改早于该阈值发生,则它会失效。 Combine 是否提供类似的服务?
免责声明:这个问题是关于一般的绑定,而不仅仅是特定的 TextField。 Textfield 只是一个编码示例,因此 .onCommit
不是我正在寻找的解决方案 ;)
Combine 中的 debounce
运算符就是这样做的。它会等到 X 时间内没有通过管道推送新值,然后发送信号。
在 TextField
的情况下,您仍然需要“正常”绑定,因为您希望用户看到实时出现的字符,即使数据没有显示立即发送到服务器(或任何其他昂贵的操作)。
import Combine
import SwiftUI
class ViewModel : ObservableObject {
@Published var text : String = ""
private var cancellables = Set<AnyCancellable>()
init() {
$text
.debounce(for: .seconds(2), scheduler: RunLoop.main)
.sink { (newValue) in
//do expensive operation
print(newValue)
}
.store(in: &cancellables)
}
}
struct ContentView : View {
@StateObject var vm = ViewModel()
var body: some View {
TextField("", text: $vm.text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
根据您的操作,您可能还想添加一个 .receive(on:)
运算符。要更新 UI,您需要在主线程上接收。但是,对于其他昂贵的操作,您可以通过这种方式将事情发送到后台队列。
当我在 SwiftUI 中有一个绑定并且我想在绑定更改时保存(例如在 TextField 上)
var myText: String { /* value is derived */ }
func save(_ text: String) { /* doing the save stuff */ }
TextFiel(text: .init(get: { myText }, set: { save([=10=]) })
这样做,只要绑定发生变化,就会调用 save()
。在某些情况下,这可能并不理想,例如当 save() 进行服务器调用或一些昂贵的计算时。所以我正在寻找的是,每当 binding 为 上次 更改时得到通知。
也许某种延迟的观察者在最终更改后触发 x 秒,如果另一个更改早于该阈值发生,则它会失效。 Combine 是否提供类似的服务?
免责声明:这个问题是关于一般的绑定,而不仅仅是特定的 TextField。 Textfield 只是一个编码示例,因此 .onCommit
不是我正在寻找的解决方案 ;)
Combine 中的 debounce
运算符就是这样做的。它会等到 X 时间内没有通过管道推送新值,然后发送信号。
在 TextField
的情况下,您仍然需要“正常”绑定,因为您希望用户看到实时出现的字符,即使数据没有显示立即发送到服务器(或任何其他昂贵的操作)。
import Combine
import SwiftUI
class ViewModel : ObservableObject {
@Published var text : String = ""
private var cancellables = Set<AnyCancellable>()
init() {
$text
.debounce(for: .seconds(2), scheduler: RunLoop.main)
.sink { (newValue) in
//do expensive operation
print(newValue)
}
.store(in: &cancellables)
}
}
struct ContentView : View {
@StateObject var vm = ViewModel()
var body: some View {
TextField("", text: $vm.text)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
根据您的操作,您可能还想添加一个 .receive(on:)
运算符。要更新 UI,您需要在主线程上接收。但是,对于其他昂贵的操作,您可以通过这种方式将事情发送到后台队列。