Связанные несколько слайдеров в SwiftUI - PullRequest
0 голосов
/ 04 февраля 2020

Мне было интересно, может ли кто-нибудь мне помочь.

В приложении, над которым я работаю, мне нужно разделить значение на четыре разные области, и я хотел бы сделать это с помощью четырех ползунков, которые контролировать процент для каждой из областей.

Для каждого Slider я создал @State private var sliderAValue = 25 и привязал их к ползункам с помощью Slider(value: $sliderAValue, in: 0...100)

Ясно, когда я изменяю значение любого из ползунков, остальные значения также должны измениться, чтобы общий процент всегда был равен 100.

Но, что более важно, соотношение между тремя другими значениями должно быть одинаковым: например, если исходное разделение составляет 30: 30: 20: 20, а пользователь изменяет третий элемент на 40%, то новое разделение должно стать 22,5: 22,5: 40: 15.

Применение didSet или willSet на переменные State, похоже, не оказывает никакого влияния, но мне явно нужно где-то хранить исходную крысу ios и автоматически обновлять их.

Есть идеи?

Ответы [ 2 ]

1 голос
/ 04 февраля 2020

Вы можете хранить привязки ко всем State переменным в массиве типа Array<Binding<Double>>. Это позволяет вам добавлять больше ползунков, если вы хотите без большого количества кода.

Synchronized Sliders

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 на:

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()
0 голосов
/ 04 февраля 2020

Вот решение с использованием onEditingChanged. Другие ползунки не обновляются, пока вы не отпустите тот, который редактируете. (Конечно, должен быть способ вывести вычисление в функцию, чтобы оно не повторялось четыре раза, но оно не приходит ко мне прямо сейчас.)

enter image description here

struct ContentView: View {
    @State private var sliderAValue: Double = 25
    @State private var sliderBValue: Double = 25
    @State private var sliderCValue: Double = 25
    @State private var sliderDValue: Double = 25

    var body: some View {
        VStack {
            // Slider A
            Slider(value: $sliderAValue, in: 0...100, onEditingChanged: { _ in
                let remaining = self.sliderBValue + self.sliderCValue + self.sliderDValue
                let percentageB = self.sliderBValue / remaining
                let percentageC = self.sliderCValue / remaining
                let percentageD = self.sliderDValue / remaining

                let available = 100 - self.sliderAValue

                self.sliderBValue = available * percentageB
                self.sliderCValue = available * percentageC
                self.sliderDValue = available * percentageD
            })
            Text("Slider A: \(sliderAValue, specifier: "%.1f")")

            // Slider B
            Slider(value: $sliderBValue, in: 0...100, onEditingChanged: { _ in
                let remaining = self.sliderAValue + self.sliderCValue + self.sliderDValue
                let percentageA = self.sliderAValue / remaining
                let percentageC = self.sliderCValue / remaining
                let percentageD = self.sliderDValue / remaining

                let available = 100 - self.sliderBValue

                self.sliderAValue = available * percentageA
                self.sliderCValue = available * percentageC
                self.sliderDValue = available * percentageD
            })
            Text("Slider B: \(sliderBValue, specifier: "%.1f")")

            // Slider C
            Slider(value: $sliderCValue, in: 0...100, onEditingChanged: { _ in
                let remaining = self.sliderAValue + self.sliderBValue + self.sliderDValue
                let percentageA = self.sliderAValue / remaining
                let percentageB = self.sliderBValue / remaining
                let percentageD = self.sliderDValue / remaining

                let available = 100 - self.sliderCValue

                self.sliderAValue = available * percentageA
                self.sliderBValue = available * percentageB
                self.sliderDValue = available * percentageD
            })
            Text("Slider C: \(sliderCValue, specifier: "%.1f")")

            // Slider D
            Slider(value: $sliderDValue, in: 0...100, onEditingChanged: { _ in
                let remaining = self.sliderAValue + self.sliderBValue + self.sliderCValue
                let percentageA = self.sliderAValue / remaining
                let percentageB = self.sliderBValue / remaining
                let percentageC = self.sliderCValue / remaining

                let available = 100 - self.sliderDValue

                self.sliderAValue = available * percentageA
                self.sliderBValue = available * percentageB
                self.sliderCValue = available * percentageC
            })
            Text("Slider D: \(sliderDValue, specifier: "%.1f")")
        }
    }
}
...