【问题标题】:Interlinked Multiple Sliders in SwiftUISwiftUI 中互连的多个滑块
【发布时间】:2020-02-04 14:57:57
【问题描述】:

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

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

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

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

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

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

有什么想法吗?

【问题讨论】:

  • 由于状态是Int 类型,在您的示例中使用 22.5 会发生什么?如果我们将 22.5 都转换为 22,则总和不会是 100。
  • 抱歉,@Qbyte,我没有说清楚。存储的值是 Double 值,但在 UI 中四舍五入为 Int
  • 如果用户将一个滑块拖动到 100 会发生什么情况,所以我们有一个 100 : 0 : 0 : 0 的分割然后到 0?应该是 0 : 33 : 33 : 33 ?
  • 谢谢@Qbyte,这看起来是一个很好的答案。
  • 认为我可以显示到小数点后 1 位 - 这不会消除您指出的问题,但会减少其影响。

标签: slider swiftui


【解决方案1】:

您可以将所有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()

【讨论】:

    【解决方案2】:

    这是一个使用 onEditingChanged 的​​解决方案。在您释放正在编辑的滑块之前,其他滑块不会更新。 (当然必须有一种方法可以将计算提取到一个函数中,这样它就不会重复四次,但它现在不会来找我。)

    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")")
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-06
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      相关资源
      最近更新 更多