I want to create a control pair similar to Logic Pro's fader: it's a Slider synchronized with a numeric TextTield. If you move the slider, the text field updates with the slider position, and if you enter something in the text field, the slider moves to a position corresponding to the new value. (Also the value gets used by the program logic but that's not the issue.)
I did this previously with AppKit and it works, and now I'm learning Swift and SwiftUI and I'm stuck.
Attached is my attempt at this. My "Fader" has clamps, so the setting will never be outside of a given range.
When I type a value in the TextField and hit <Return> the Slider updates as expected. The operation in the other direction doesn't work: when I move the Slider, the TextField doesn't update. I know the properties are updating because the print statements tell me so.
My code below has one @State property for the slider and another for the text field. I did this because if they shared the value property, the slider would move after each keypress in the text field, basically ignoring onSubmit. I found an example that used an ObservableObject class with one Published property and that property was used as the value: for both the slider and text field and that failed in the same was as my code.
What am I missing? Thanks in advance to anyone who can help.
struct SliderWithEditView: View {
private var rangeMin : Float = -100.0
private var rangeMax : Float = 100.0
@State private var textValue : Float
@State private var sliderValue : Float
/* accept default initializers */
init() {
textValue = rangeMin
sliderValue = rangeMin
}
/* user initializers from instantiation */
init(min : Float, max: Float) {
rangeMin = min
rangeMax = max
textValue = min
sliderValue = min
print("Starting values:\nrangeMin = \(rangeMin) to rangeMax = \(rangeMax)")
print("textValue = \(textValue)\tsliderValue = \(sliderValue)")
}
var body: some View {
VStack {
TextField("Value:",
value: $textValue,
format: .number.precision(.fractionLength(1)))
.frame(width: 50, height: 50)
.onSubmit {
print("New value = \(textValue)")
if textValue > rangeMax {
textValue = rangeMax
} else if textValue < rangeMin {
textValue = rangeMin
}
sliderValue = textValue
print("New slider value after text entry: \(sliderValue)")
}
.disableAutocorrection(true)
Slider(value: $sliderValue,
in: rangeMin...rangeMax,
step: 0.5,
onEditingChanged: { editing in
if editing == false {
textValue = sliderValue
print("After slide stops, sliderValue = \(sliderValue)\ttextValue = \(textValue)")
}
})
.frame(width: 200, height: 10)
.padding()
}
}
}
struct SliderWithEditView_Previews: PreviewProvider {
static var previews: some View {
SliderWithEditView()
}
}