在 SwiftUI 中捕获绑定的最后一次更改 / 组合

Posted

技术标签:

【中文标题】在 SwiftUI 中捕获绑定的最后一次更改 / 组合【英文标题】:Capture the Very Last Change of a Binding in SwiftUI / Combine 【发布时间】:2021-03-10 05:44:52 【问题描述】:

当我在 SwiftUI 中有一个绑定并且我想在绑定更改时保存(例如在 TextField 上)

var myText: String  /* value is derived */ 
func save(_ text: String)  /* doing the save stuff */ 
TextFiel(text: .init(get:  myText , set:  save($0) ) 

这样做,save() 会在绑定发生变化时被调用。在某些情况下,这可能并不理想,例如当 save() 进行服务器调用或一些昂贵的计算时。所以我正在寻找的是,每当最后一次绑定发生变化时,我都会收到通知。 也许某种延迟的观察者会在最终更改后 x 秒触发,如果另一个更改发生在该阈值之前,则会失效。 Combine 是否提供这样的服务?

免责声明:这个问题是关于一般的绑定,而不仅仅是关于 TextFields。 Textfield 只是一个编码示例,所以.onCommit 不是我正在寻找的解决方案;)

【问题讨论】:

【参考方案1】:

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,您需要在主线程上接收。但是,对于其他昂贵的操作,您可以通过这种方式将内容发送到后台队列。

【讨论】:

去抖动是我所寻找的 100%。谢谢!

以上是关于在 SwiftUI 中捕获绑定的最后一次更改 / 组合的主要内容,如果未能解决你的问题,请参考以下文章

iOS 15 SwiftUI 3 Picker 绑定在更改@State 值后不起作用

如何根据布尔值 (SwiftUI) 更改对象的绑定源?

为啥我的绑定 [String] 不会更改我的多选列表 SwiftUI

SwiftUI:绑定到@AppStorage

来自 @Published 属性的 SwiftUI 动画从视图外部更改

SwiftUI 实时预览中的可变绑定