带有 CurrentValueSubject 绑定的 TextField 上的“Binding<String> 操作尝试每帧更新多次”
Posted
技术标签:
【中文标题】带有 CurrentValueSubject 绑定的 TextField 上的“Binding<String> 操作尝试每帧更新多次”【英文标题】:"Binding<String> action tried to update multiple times per frame" on TextField with CurrentValueSubject binding 【发布时间】:2021-10-09 09:04:47 【问题描述】:我一直在尝试找到一种将TextField
与视图模型连接起来的好方法,以便TextField
的输入不会立即更新视图。
这是一个工作示例:
struct TextFieldSamples: View
@StateObject var vm = TFViewModel()
var body: some View
print(Self._printChanges())
return VStack
TextField("placeholder",
text: Binding(
get: vm.outputText ,
set: vm.subject.value = $0
))
Text(vm.outputText) // this will only update when the textfield holds >5 characters
class TFViewModel: ObservableObject
@Published var outputText: String = ""
let subject = CurrentValueSubject<String, Never>("")
var subscriptions = Set<AnyCancellable>()
init()
subject
.filter( $0.count > 5 )
.sink self.outputText = $0
.store(in: &subscriptions)
但是,我在 swiftbysundell.com 博客上看到了下面的 propertyWrapped CurrentValueSubject
@propertyWrapper
struct Input<Value>
var wrappedValue: Value
get subject.value
set subject.send(newValue)
var projectedValue: AnyPublisher<Value, Never>
subject.eraseToAnyPublisher()
private let subject: CurrentValueSubject<Value, Never>
init(wrappedValue: Value)
subject = CurrentValueSubject(wrappedValue)
尝试使用,会报错Binding<String> action tried to update multiple times per frame
。
(不仅如此,TextField
的输入行为也会变得混乱。这尤其体现在日语输入中,字符转换在大多数情况下都不起作用。)
这是一个显示此行为的示例:
struct TextFieldSamples: View
@StateObject var vm = TFViewModel()
var body: some View
print(Self._printChanges())
return VStack
TextField("placeholder", text: $vm.inputText)
Text(vm.outputText)
class TFViewModel: ObservableObject
@Input var inputText: String = ""
@Published var outputText: String = ""
var subscriptions = Set<AnyCancellable>()
init()
$inputText
.filter( $0.count > 5 )
.sink self.outputText = $0
.store(in: &subscriptions)
我不太明白为什么 PropertyWrapper 版本没有按预期工作。
【问题讨论】:
你试过.debounce(for:)
吗?将速率保持在导致错误的阈值以下可能就足够了。
谢谢@Yrb,我试过了。 Binding<String>
消息仍然显示。
【参考方案1】:
问题似乎是绑定到vm
的inputText
字段和绑定到vm
本身作为@StateObject
的组合。
当输入文本时,它将值发送到inputText
绑定。这允许系统标记TextField
需要重新评估。
但inputText
是vm
对象的结构属性。因为vm
是@StateObject
,当inputText发生变化时,vm
对象广播TextFieldSamples
视图需要重新评估。作为其中的一部分,它还尝试设置textInput
的值(设置为它已经拥有的值),这会导致“绑定在同一帧中多次触发”消息。
您可以通过将vm
设置为@State
属性而不是@StateObject
来消除绑定触发的额外时间。现在对inputText
的更改将触发一次,vm
不会广播第二次尝试触发它的对象更改消息。
但是,当您这样做时,系统不会知道 TextFieldSamples
需要重新评估,因此它永远不会看到对 outputText
的更改。
【讨论】:
以上是关于带有 CurrentValueSubject 绑定的 TextField 上的“Binding<String> 操作尝试每帧更新多次”的主要内容,如果未能解决你的问题,请参考以下文章
什么是 PassthroughSubject 和 CurrentValueSubject
CurrentValueSubject 和 @Published 之间的区别
Combine - 测试 CurrentValueSubject 是不是有订阅者