ReactiveCocoa 的循环绑定,如何将一个值绑定到两个输入 UI 元素
Cyclic Bindings with ReactiveCocoa, How to bind one value to two input UI elements
在一个 iOS 项目中,我必须实现一个视图控制器,允许同时使用滑块 和 文本字段来选择浮点值。它应该具有以下行为:
- 每当在文本字段中键入数字时,滑块应将其值更新为键入的浮点数
- 无论何时拖动滑块,它都应使用滑块值更新文本字段
目前思路如下:
我在视图模型中有两个属性,实际值和值文本。
每当更新值时,我都会更新视图模型中的文本。
文本字段和滑块都只更改值。
但后来我有一种 'cyclic' 依赖:文本字段更新视图模型值,它更新视图模型文本,它绑定到我们刚刚键入值的文本字段。这会导致文本字段出现错误行为。我尝试使用 isFirstResponder 属性,效果更好,但似乎不是最佳实践,而且它不是预期的。
有没有办法将滑块的 enabledProperty 绑定到文本字段的 isFirstResponder?那也行
VC 中的绑定:
layout.valueTextField.reactive.text <~ viewModel.valueText.map( { text in
if !self.layout.valueTextField.isFirstResponder {
return text
}
return self.layout.valueTextField.text ?? ""
}
)
viewModel.value <~ layout.valueSlider.reactive.values
layout.valueSlider.reactive.value <~ viewModel.value
viewModel.value <~ layout.valueTextField.reactive.continuousTextValues.map({ textValue in
if let t = textValue {
if let parsed = Float(t) {
return parsed
}
}
return viewModel.value.value
})
我的视图模型:
import ReactiveSwift
class ValuePickerViewModel {
var valueText: MutableProperty<String>
var value: MutableProperty<Float>
let minValue: Float
let maxValue: Float
let unit: String
init(initialValue: Float, formatter: FloatFormatter, minValue: Float, maxValue: Float, unit: String) {
self.value = MutableProperty(initialValue)
self.valueText = MutableProperty(formatter.format(value: initialValue))
self.valueText <~ self.value.map({value in
formatter.format(value: value)
}
)
self.minValue = minValue
self.maxValue = maxValue
self.unit = unit
}
}
解决方法很简单:
textFields 值绑定到视图模型的浮点值。
幻灯片值绑定到视图模型文本值。
所以每个组件更新另一个,没有循环!
工作代码:
绑定:
viewModel.value <~ layout.valueTextField.reactive.continuousTextValues.map({ textValue in
if let t = textValue {
if let parsed = Float(t) {
return parsed
}
}
return viewModel.value.value
})
viewModel.valueText <~ layout.valueSlider.reactive.values.map({ value in
return viewModel.formatter.format(value: value)
})
layout.valueSlider.reactive.value <~ viewModel.value
layout.valueTextField.reactive.text <~ viewModel.valueText
视图模型:
import ReactiveSwift
class ValuePickerViewModel {
var valueText: MutableProperty<String>
var value: MutableProperty<Float>
let minValue: Float
let maxValue: Float
let unit: String
let formatter: FloatFormatter
init(initialValue: Float, formatter: FloatFormatter, minValue: Float, maxValue: Float, unit: String) {
self.formatter = formatter
self.value = MutableProperty(initialValue)
self.valueText = MutableProperty(formatter.format(value: initialValue))
self.minValue = minValue
self.maxValue = maxValue
self.unit = unit
}
}
在一个 iOS 项目中,我必须实现一个视图控制器,允许同时使用滑块 和 文本字段来选择浮点值。它应该具有以下行为:
- 每当在文本字段中键入数字时,滑块应将其值更新为键入的浮点数
- 无论何时拖动滑块,它都应使用滑块值更新文本字段
目前思路如下:
我在视图模型中有两个属性,实际值和值文本。 每当更新值时,我都会更新视图模型中的文本。 文本字段和滑块都只更改值。 但后来我有一种 'cyclic' 依赖:文本字段更新视图模型值,它更新视图模型文本,它绑定到我们刚刚键入值的文本字段。这会导致文本字段出现错误行为。我尝试使用 isFirstResponder 属性,效果更好,但似乎不是最佳实践,而且它不是预期的。 有没有办法将滑块的 enabledProperty 绑定到文本字段的 isFirstResponder?那也行
VC 中的绑定:
layout.valueTextField.reactive.text <~ viewModel.valueText.map( { text in
if !self.layout.valueTextField.isFirstResponder {
return text
}
return self.layout.valueTextField.text ?? ""
}
)
viewModel.value <~ layout.valueSlider.reactive.values
layout.valueSlider.reactive.value <~ viewModel.value
viewModel.value <~ layout.valueTextField.reactive.continuousTextValues.map({ textValue in
if let t = textValue {
if let parsed = Float(t) {
return parsed
}
}
return viewModel.value.value
})
我的视图模型:
import ReactiveSwift
class ValuePickerViewModel {
var valueText: MutableProperty<String>
var value: MutableProperty<Float>
let minValue: Float
let maxValue: Float
let unit: String
init(initialValue: Float, formatter: FloatFormatter, minValue: Float, maxValue: Float, unit: String) {
self.value = MutableProperty(initialValue)
self.valueText = MutableProperty(formatter.format(value: initialValue))
self.valueText <~ self.value.map({value in
formatter.format(value: value)
}
)
self.minValue = minValue
self.maxValue = maxValue
self.unit = unit
}
}
解决方法很简单: textFields 值绑定到视图模型的浮点值。 幻灯片值绑定到视图模型文本值。 所以每个组件更新另一个,没有循环!
工作代码:
绑定:
viewModel.value <~ layout.valueTextField.reactive.continuousTextValues.map({ textValue in
if let t = textValue {
if let parsed = Float(t) {
return parsed
}
}
return viewModel.value.value
})
viewModel.valueText <~ layout.valueSlider.reactive.values.map({ value in
return viewModel.formatter.format(value: value)
})
layout.valueSlider.reactive.value <~ viewModel.value
layout.valueTextField.reactive.text <~ viewModel.valueText
视图模型:
import ReactiveSwift
class ValuePickerViewModel {
var valueText: MutableProperty<String>
var value: MutableProperty<Float>
let minValue: Float
let maxValue: Float
let unit: String
let formatter: FloatFormatter
init(initialValue: Float, formatter: FloatFormatter, minValue: Float, maxValue: Float, unit: String) {
self.formatter = formatter
self.value = MutableProperty(initialValue)
self.valueText = MutableProperty(formatter.format(value: initialValue))
self.minValue = minValue
self.maxValue = maxValue
self.unit = unit
}
}