SwiftUI:如何通过拇指移动滑块的值

3
我想在SwiftUI中创建带有值标签的滑块。
我已经编写了以下代码。
struct SliderView: View {
    @State private var speed = 50.0
    var minValue: Double = 1
    var maxValue: Double = 100
    var body: some View {
        VStack {
            Text("\(speed,specifier: "%.f")")
                .foregroundColor(.blue)

            Slider(
                value: $speed,
                in: minValue...maxValue,
                step: 1
            ) {
                Text("Speed")
            } minimumValueLabel: {
                Text("\(minValue,specifier: "%.f")")
            } maximumValueLabel: {
                Text("\(maxValue,specifier: "%.f")")
            }

        }.padding()
    }
}

但我想要将值标签与滑块拇指一起移动。

我找不到任何可设置Y值的.frame属性。

问题:

如何将滑块值标签与拇指一起移动,并随着拇指变化移动标签值。

enter image description here

enter image description here

enter image description here

3个回答

5
这里的问题是我们无法访问nob的大小和位置(这些是私有的),只能参考范围和当前值,这导致滑块nob和悬停值之间存在一些不对齐。
无论如何,这里有一个可能的方法,使用对齐指南。nob大小的调整留给您来完成。
在Xcode 13.4 / iOS 15.5上进行了测试。

demo

struct SliderView: View {
    @State private var speed = 50.0

    var minValue: Double = 1
    var maxValue: Double = 100

    var body: some View {
        HStack {
            Text("\(minValue,specifier: "%.f")")
            Slider(value: $speed, in: minValue...maxValue, step: 1)
                .alignmentGuide(VerticalAlignment.center) { $0[VerticalAlignment.center]}
                .padding(.top)
                .overlay(GeometryReader { gp in
                    Text("\(speed,specifier: "%.f")").foregroundColor(.blue)
                        .alignmentGuide(HorizontalAlignment.leading) {
                            $0[HorizontalAlignment.leading] - (gp.size.width - $0.width) * speed / ( maxValue - minValue)
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)
                }, alignment: .top)
            Text("\(maxValue,specifier: "%.f")")
        }
        .padding()
    }
}

在GitHub上的测试模块


一切都很好,直到您的minValue为1。但是如果minValue不同,则位置非常糟糕。 - Kudos
请帮我一下。 - Kudos
@Asperi为什么你用已经存在的相同值来调用alignmentGuide?.alignmentGuide(VerticalAlignment.center) { $0[VerticalAlignment.center]} - Amadeu Cavalcante Filho

1

首先,您需要读取您的Slider的宽度,为此您将需要一个GeometryReader。使用Slider的当前值,您可以近似拇指的位置。然后,您可以偏移Text

struct SliderView: View {
    @State private var speed = 50.0
    var minValue: Double = 1
    var maxValue: Double = 100
    @State var width: CGFloat = 0
    var body: some View {
        VStack {
            Text("\(speed,specifier: "%.f")")
                .foregroundColor(.blue)
                .offset(x: width*speed/100)
            GeometryReader { proxy in
                Slider(
                    value: $speed,
                    in: minValue...maxValue,
                    step: 1
                ) {
                    Text("Speed")
                } minimumValueLabel: {
                    Text("\(minValue,specifier: "%.f")")
                } maximumValueLabel: {
                    Text("\(maxValue,specifier: "%.f")")
                }.onAppear {
                    self.width = proxy.frame(in: .global).width
                }
            }
        }.padding()
    }
}

0

我尝试了@Asperi答案,他做得非常好,只需要进行一些更新:

问题:

  1. 一切都很好,直到您的minValue为1。但是,如果minValue大于1,则浮动标签的位置不同。

更新代码:

struct SliderView: View {
    @State var speed: Double
    var minValue: Double = 50
    var maxValue: Double = 100
    init(minVal: Double, maxVal: Double) {
        self.minValue = minVal
        self.maxValue = maxVal
        self.speed = minVal
    }
    
    var body: some View {
        HStack {
            Text("\(minValue,specifier: "%.f")")
            Slider(value: $speed, in: minValue...maxValue, step: 1)
                .alignmentGuide(VerticalAlignment.center) { $0[VerticalAlignment.center]}
                .padding(.top)
                .overlay(GeometryReader { gp in
                    VStack {
                    Text("\(speed,specifier: "%.f")").foregroundColor(.blue)
                        .alignmentGuide(HorizontalAlignment.leading) {
                            print("Hii \(speed) $0.width: \($0.width)")
                            return $0[HorizontalAlignment.leading] - (gp.size.width - $0.width) * (speed - minValue) / ( maxValue - minValue)
                        }
                        .frame(maxWidth: .infinity, alignment: .leading)
                }
                }, alignment: .top)
            Text("\(maxValue,specifier: "%.f")")
        }
        .padding()
    }
}

使用方法:

 SliderView(minVal: 0, maxVal: 100)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接