如何让两个 SwiftUI 滑块更新到第三个值或从第三个值更新,第三个值代表它们各自值的“单一真实来源”
How to get two SwiftUI sliders to update to and from a third value which represents the ‘single source of truth’ for their respective values
我是 Combine 的新手,我正在尝试让两个 SwiftUI 滑块在第三个值之间进行更新,第三个值代表它们各自值的“单一真实来源”。
每个滑块的范围为 0…1,但使用不同的函数来确定其值。在下面的示例中,如果真实值为 0.5,则第一个滑块直接设置并获取该值 (0.5),而第二个滑块使用值的平方 (0.25)。
该示例有效,但我的印象是这并不是真正正确的方法。如果在模型中删除了保护语句,我将得到无限递归。任何人都可以告诉我正确的方法吗?谢谢!
这是模型:
import SwiftUI
class Model : ObservableObject{
@Published var trueValue: CGFloat = 0{
didSet{
slider1Value = trueValue
slider2Value = trueValue * trueValue
}
}
@Published var slider1Value: CGFloat = 0{
didSet{
guard oldValue != slider1Value else { return }
trueValue = slider1Value
}
}
@Published var slider2Value: CGFloat = 0{
didSet{
guard oldValue != slider2Value else { return }
trueValue = slider2Value.squareRoot()
}
}
}
这是内容视图:
struct ContentView: View {
@EnvironmentObject var model: Model
var body: some View {
GeometryReader{ geometry in
VStack{
ProportionalSlider(value: self.$model.slider1Value, width: geometry.size.width - 20)
.frame(width: geometry.size.width, height: 18)
ProportionalSlider(value: self.$model.slider2Value, width: geometry.size.width - 20)
.frame(width: geometry.size.width, height: 18)
}
}
}
}
这是 ProportionalSlider:
struct ProportionalSlider: View {
@Binding var value: CGFloat
var width: CGFloat
var body: some View{
let gesture = DragGesture(minimumDistance: 0)
.onChanged({ mouse in
let clamped = min(self.width, max(0, mouse.location.x))
self.value = clamped/self.width
})
return GeometryReader{ geometry in
ZStack{
ZStack(alignment: .leading){
Capsule().fill(Color.blue).frame(width: self.width, height: 15)
.gesture(gesture)
Capsule().fill(Color.red).frame(width: self.value * self.width, height: 15)
.gesture(gesture)
}
}
}
}
}
使用一个 @Published
属性 和一个计算 属性:
class Model: ObservedObject {
@Published var linearValue: CGFloat = 0
var quadraticValue: CGFloat {
get { linearValue * linearValue }
set { linearValue = sqrt(newValue) }
}
}
将一个滑块绑定到 $model.linearValue
,将另一个滑块绑定到 $model.quadraticValue
。
我是 Combine 的新手,我正在尝试让两个 SwiftUI 滑块在第三个值之间进行更新,第三个值代表它们各自值的“单一真实来源”。
每个滑块的范围为 0…1,但使用不同的函数来确定其值。在下面的示例中,如果真实值为 0.5,则第一个滑块直接设置并获取该值 (0.5),而第二个滑块使用值的平方 (0.25)。
该示例有效,但我的印象是这并不是真正正确的方法。如果在模型中删除了保护语句,我将得到无限递归。任何人都可以告诉我正确的方法吗?谢谢!
这是模型:
import SwiftUI
class Model : ObservableObject{
@Published var trueValue: CGFloat = 0{
didSet{
slider1Value = trueValue
slider2Value = trueValue * trueValue
}
}
@Published var slider1Value: CGFloat = 0{
didSet{
guard oldValue != slider1Value else { return }
trueValue = slider1Value
}
}
@Published var slider2Value: CGFloat = 0{
didSet{
guard oldValue != slider2Value else { return }
trueValue = slider2Value.squareRoot()
}
}
}
这是内容视图:
struct ContentView: View {
@EnvironmentObject var model: Model
var body: some View {
GeometryReader{ geometry in
VStack{
ProportionalSlider(value: self.$model.slider1Value, width: geometry.size.width - 20)
.frame(width: geometry.size.width, height: 18)
ProportionalSlider(value: self.$model.slider2Value, width: geometry.size.width - 20)
.frame(width: geometry.size.width, height: 18)
}
}
}
}
这是 ProportionalSlider:
struct ProportionalSlider: View {
@Binding var value: CGFloat
var width: CGFloat
var body: some View{
let gesture = DragGesture(minimumDistance: 0)
.onChanged({ mouse in
let clamped = min(self.width, max(0, mouse.location.x))
self.value = clamped/self.width
})
return GeometryReader{ geometry in
ZStack{
ZStack(alignment: .leading){
Capsule().fill(Color.blue).frame(width: self.width, height: 15)
.gesture(gesture)
Capsule().fill(Color.red).frame(width: self.value * self.width, height: 15)
.gesture(gesture)
}
}
}
}
}
使用一个 @Published
属性 和一个计算 属性:
class Model: ObservedObject {
@Published var linearValue: CGFloat = 0
var quadraticValue: CGFloat {
get { linearValue * linearValue }
set { linearValue = sqrt(newValue) }
}
}
将一个滑块绑定到 $model.linearValue
,将另一个滑块绑定到 $model.quadraticValue
。