SwiftUI 中相互关联的多个滑块 [英] Interlinked Multiple Sliders in SwiftUI
问题描述
我想知道是否有人可以帮助我.
I was wondering whether anyone could help me.
在我正在开发的应用程序中,我需要将一个值分成四个不同的区域,我想通过四个滑块来实现,这些滑块控制每个区域的百分比.
In the app I'm working on, I need to split a value into four different areas and I'd like to do so via four sliders, which control the percentage for each of the areas.
对于每个 Slider
,我创建了一个 @State private var sliderAValue = 25
并通过 Slider(value: $sliderAValue,在:0...100)
For each Slider
, I've created an @State private var sliderAValue = 25
and bound them to the sliders via Slider(value: $sliderAValue, in: 0...100)
显然,当我更改任何滑块的值时,其他值也应更改,以使总百分比始终为 100.
Clearly, when I change the value of any of the sliders, the other values should also change in order that the total percentage is always 100.
但更重要的是,其他三个值之间的比率应该是相同的:例如.如果原来的分割是 30 : 30 : 20 : 20 并且用户将第三个元素更改为 40%,那么新的分割应该变成 22.5 : 22.5 : 40 : 15.
But more importantly, the ratio between the other three values should state the same: eg. if the original split is 30 : 30 : 20 : 20 and the user changes the third element to 40%, then the new split should become 22.5 : 22.5 : 40 : 15.
在 State
变量上应用 didSet
或 willSet
似乎没有任何效果,但我显然需要存储原始比率,并自动更新它们.
Applying a didSet
or a willSet
on the State
variables doesn't seem to have any effect, yet I clearly need to store the original ratios somewhere and have them automatically updated.
有什么想法吗?
推荐答案
您可以将所有 State
变量的绑定存储在 Array<Binding<Double>>代码>.如果您愿意,这允许您添加更多滑块而无需更多代码.
You can store the bindings to all State
variables in an array of type Array<Binding<Double>>
. This allows you to add more sliders if you want without much more code.
struct Sliders: View {
@State var value1 = 100.0
@State var value2 = 0.0
@State var value3 = 0.0
@State var value4 = 0.0
var body: some View {
// add all bindings which you want to synchronize
let allBindings = [$value1, $value2, $value3, $value4]
return VStack {
Button(action: {
// Manually setting the values does not change the values such
// that they sum to 100. Use separate algorithm for this
self.value1 = 10
self.value2 = 40
}) {
Text("Test")
}
Text("(value1)")
synchronizedSlider(from: allBindings, index: 0)
Text("(value2)")
synchronizedSlider(from: allBindings, index: 1)
Text("(value3)")
synchronizedSlider(from: allBindings, index: 2)
Text("(value4)")
synchronizedSlider(from: allBindings, index: 3)
}.padding()
}
func synchronizedSlider(from bindings: [Binding<Double>], index: Int) -> some View {
return Slider(value: synchronizedBinding(from: bindings, index: index),
in: 0...100)
}
func synchronizedBinding(from bindings: [Binding<Double>], index: Int) -> Binding<Double> {
return Binding(get: {
return bindings[index].wrappedValue
}, set: { newValue in
let sum = bindings.indices.lazy.filter{ $0 != index }.map{ bindings[$0].wrappedValue }.reduce(0.0, +)
// Use the 'sum' below if you initially provide values which sum to 100
// and if you do not set the state in code (e.g. click the button)
//let sum = 100.0 - bindings[index].wrappedValue
let remaining = 100.0 - newValue
if sum != 0.0 {
for i in bindings.indices {
if i != index {
bindings[i].wrappedValue = bindings[i].wrappedValue * remaining / sum
}
}
} else {
// handle 0 sum
let newOtherValue = remaining / Double(bindings.count - 1)
for i in bindings.indices {
if i != index {
bindings[i].wrappedValue = newOtherValue
}
}
}
bindings[index].wrappedValue = newValue
})
}
}
如果您只想同步 value1
/value2
和 value3
/value4
的滑块,您可以更改body
中的代码到:
If you want to synchronize only the sliders for value1
/value2
and value3
/value4
you can change the code in body
to:
let bindings12 = [$value1, $value2]
let bindings34 = [$value3, $value4]
return VStack {
Text("(value1)")
synchronizedSlider(from: bindings12, index: 0)
Text("(value2)")
synchronizedSlider(from: bindings12, index: 1)
Text("(value3)")
synchronizedSlider(from: bindings34, index: 0)
Text("(value4)")
synchronizedSlider(from: bindings34, index: 1)
}.padding()
这篇关于SwiftUI 中相互关联的多个滑块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!