首页 > 解决方案 > SwiftUI 中相互关联的多个滑块

问题描述

我想知道是否有人可以帮助我。

在我正在开发的应用程序中,我需要将一个值分成四个不同的区域,我想通过四个滑块来实现,这些滑块控制每个区域的百分比。

对于每一个Slider,我都创建了一个@State private var sliderAValue = 25并将它们绑定到滑块上Slider(value: $sliderAValue, in: 0...100)

显然,当我更改任何滑块的值时,其他值也应该更改,以便总百分比始终为 100。

但更重要的是,其他三个值之间的比率应该是相同的:例如。如果原来的分割是 30 : 30 : 20 : 20 并且用户将第三个元素更改为 40%,那么新的分割应该变成 22.5 : 22.5 : 40 : 15。

对变量应用 adidSet或 a似乎没有任何效果,但我显然需要将原始比率存储在某处并自动更新它们。 willSetState

有任何想法吗?

标签: sliderswiftui

解决方案


您可以将所有State变量的绑定存储在类型数组中Array<Binding<Double>>。这允许您在没有更多代码的情况下添加更多滑块。

同步滑块

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/value2value3/的滑块,value4可以将代码更改body为:

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()

推荐阅读