如何让两个 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