Reputation: 1190
In an iOS Project, i have to implement a view controller that allows to pick a float value using a slider and a text field at the same time. it should have the following behaviour:
Current ideas is as follows:
I have two propertys in the view model, the actual value and the value text. Whenever the value is updated i update the text within the view model. Both the text field and the slider change only the value. But then i have a kind of 'cyclic' dependency: text field updates the view models value, which updates the view models text, which is bound to the textfield where we just type the value. This leads to buggy behaviour of the text field. I tried to work around with the isFirstResponder property, that works better, but does not seem best practise, plus it's not as intended. Is there a way bind the enabledProperty of the slider to isFirstResponder of the text field? That would also work
Bindings in 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
})
My view Model:
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
}
}
Upvotes: 0
Views: 529
Reputation: 1190
The solution is simple: The textFields value binds to the view models float value. The slides value binds to the view models text value. So each component updates the other, and there is no cycle!
Working code:
Bindings:
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
ViewModel:
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
}
}
Upvotes: 1