SwiftUI 中互连的多个滑块 [英] Interlinked Multiple Sliders in SwiftUI

查看:25
本文介绍了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 变量上应用 didSetwillSet 似乎没有任何效果,但我显然需要存储原始比率并让它们自动更新.

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/value2value3/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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆