SwiftUI文本防抖实现及视图关闭时的值同步问题
问题
我正在开发一个可自动保存到服务器的文本编辑字段,为此编写了一个辅助视图来简化这类视图的防抖逻辑。该视图需要支持初始值以及文本字段外部生成的新值。目前代码运行正常,但存在一个问题:视图关闭时,debouncedText不会更新。由于防抖时长设置为10秒,很容易出现视图关闭后debouncedText未完成写入的情况。
class TextDebounceObserver : ObservableObject { @Published var debouncedText = "" @Published var text = "" init(delay: DispatchQueue.SchedulerTimeType.Stride) { $text .debounce(for: delay, scheduler: DispatchQueue.main) .dropFirst() .assign(to: &$debouncedText) } } struct DebouncingTextEditor: View { @State private var textObserver: TextDebounceObserver @Binding var debouncedText: String init(debouncedText: Binding<String>, delay: DispatchQueue.SchedulerTimeType.Stride) { self._debouncedText = debouncedText self.textObserver = TextDebounceObserver(delay: delay) self.textObserver.text = debouncedText.wrappedValue } var body: some View { TextEditor(text: $textObserver.text) .onReceive(textObserver.$debouncedText.dropFirst()) { (newText) in debouncedText = newText } .onChange(of: debouncedText) { oldValue, newValue in self.textObserver.text = newValue } } }
能否通过Combine框架检测防抖操作的取消/初始化时机,在此时手动设置debouncedText?
使用示例:
DebouncingTextEditor(debouncedText: $saveText, delay: .seconds(10)) .onChange(of: viewModel.proposalMessage.body) { saveMessageToServer() }
解决方案
可以通过Combine的订阅生命周期管理,在视图销毁或防抖订阅取消时,手动将当前未提交的文本同步到debouncedText,具体修改如下:
1. 改造TextDebounceObserver,添加同步逻辑与订阅管理
修改观察者类,保存防抖订阅的引用,提供手动同步方法,并在对象销毁时自动触发同步:
import Combine class TextDebounceObserver : ObservableObject { @Published var debouncedText = "" @Published var text = "" private var cancellables = Set<AnyCancellable>() private let delay: DispatchQueue.SchedulerTimeType.Stride init(delay: DispatchQueue.SchedulerTimeType.Stride) { self.delay = delay setupDebounce() } private func setupDebounce() { cancellables.removeAll() // 清空旧订阅 $text .debounce(for: delay, scheduler: DispatchQueue.main) .dropFirst() .assign(to: \.debouncedText, on: self) .store(in: &cancellables) } // 手动同步当前文本到debouncedText func flushPendingText() { cancellables.removeAll() // 取消当前防抖等待 debouncedText = text // 直接同步未提交内容 setupDebounce() // 重新初始化防抖逻辑 } deinit { flushPendingText() // 对象销毁时兜底同步 } }
2. 在视图消失时触发同步
修改DebouncingTextEditor,利用onDisappear回调主动触发文本同步,确保视图关闭时立即更新绑定值:
struct DebouncingTextEditor: View { @State private var textObserver: TextDebounceObserver @Binding var debouncedText: String init(debouncedText: Binding<String>, delay: DispatchQueue.SchedulerTimeType.Stride) { self._debouncedText = debouncedText self.textObserver = TextDebounceObserver(delay: delay) self.textObserver.text = debouncedText.wrappedValue } var body: some View { TextEditor(text: $textObserver.text) .onReceive(textObserver.$debouncedText.dropFirst()) { newText in debouncedText = newText } .onChange(of: debouncedText) { _, newValue in self.textObserver.text = newValue } .onDisappear { textObserver.flushPendingText() // 视图消失时强制同步 } } }
逻辑说明
- 视图消失时调用
flushPendingText,直接取消防抖等待,将当前编辑的文本同步到debouncedText,避免10秒延迟。 TextDebounceObserver的deinit方法作为兜底,确保即使视图销毁逻辑未触发,对象回收时也能完成文本同步。- 重新调用
setupDebounce是为了保证如果视图后续重新出现,防抖逻辑能正常恢复工作。
内容的提问来源于stack exchange,提问作者CalebK
相关产品推荐
相关产品推荐

