如何在 SwiftUI 中为 Int 类型属性创建滑块?

问题描述 投票:0回答:4

我有一个带有名为“score”的 Int 属性的视图,我想用滑块进行调整。

struct IntSlider: View {
    @State var score:Int = 0

    var body: some View {
        VStack{
            Text(score.description)
            Slider(value: $score, in: 0.0...10.0, step: 1.0)
        }
    }
}

但 SwiftUI 的滑块仅适用于双精度/浮点数。

如何让它与我的整数一起工作?

swiftui slider
4个回答
27
投票
struct IntSlider: View {
    @State var score: Int = 0
    var intProxy: Binding<Double>{
        Binding<Double>(get: {
            //returns the score as a Double
            return Double(score)
        }, set: {
            //rounds the double to an Int
            print($0.description)
            score = Int($0)
        })
    }
    var body: some View {
        VStack{
            Text(score.description)
            Slider(value: intProxy , in: 0.0...10.0, step: 1.0, onEditingChanged: {_ in
                print(score.description)
            })
        }
    }
}

21
投票

TL;博士

作为本页其他答案的替代方案,我提出了一种解决方案,该解决方案利用类型约束的通用扩展方法来简化调用站点。这是您需要做的所有事情的示例:

Slider(value: .convert(from: $count), in: 1...8, step: 1)

这种方法的好处:

  1. 它会自动将 any

    Int
    类型(例如
    Int
    Int8
    Int64
    )转换为 any
    Float
    类型(例如
    Float
    Float16
    CGFloat 
    Double
    )、,反之亦然,感谢泛型/重载。

  2. 将扩展添加到项目(或项目引用的包中)后,您只需在

    any 绑定站点调用 .convert,它就会“正常工作”。
    

  3. 不需要其他任何东西来使用它(即没有“代理”结构、变量或其他本地项目来扰乱您的视图。)您只需直接内联使用它(见上文)

  4. 当在任何需要

    Binding

     的地方时,只需键入 
    .
    ,代码完成就会自动建议 
    convert
     作为选项。这是有效的,因为每个扩展方法都是在 
    Binding
     上定义的,并且它返回 
    Binding
    (即 
    Self
    )作为其结果类型,从而使其可以被自动完成系统发现。 (对于任何类型的任何此类静态方法都是如此。)

这里是前面提到的扩展方法...

public extension Binding { static func convert<TInt, TFloat>(from intBinding: Binding<TInt>) -> Binding<TFloat> where TInt: BinaryInteger, TFloat: BinaryFloatingPoint{ Binding<TFloat> ( get: { TFloat(intBinding.wrappedValue) }, set: { intBinding.wrappedValue = TInt($0) } ) } static func convert<TFloat, TInt>(from floatBinding: Binding<TFloat>) -> Binding<TInt> where TFloat: BinaryFloatingPoint, TInt: BinaryInteger { Binding<TInt> ( get: { TInt(floatBinding.wrappedValue) }, set: { floatBinding.wrappedValue = TFloat($0) } ) } }
...这是一个可在操场上使用的演示,展示它们的使用情况。


(注意:不要忘记复制上面的扩展方法!)

struct ConvertTestView: View { @State private var count: Int = 1 var body: some View { VStack{ HStack { ForEach(1...count, id: \.self) { n in Text("\(n)") .font(.title).bold().foregroundColor(.white) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.blue) } } .frame(maxHeight: 64) HStack { Text("Count: \(count)") Slider(value: .convert(from: $count), in: 1...8, step: 1) } } .padding() } }
最后,这是结果......


1
投票
还有一种方法是使用 double 值,然后将其转换为 int。注意:你必须小心这种方式 - 你需要记住它是一个 double 值而不是 int。

struct IntSlider: View { @State var score: Double = 0 var body: some View { VStack{ Text("\(Int(score))") Slider(value: $score, in: 0...10, step: 1) } } }
    

-1
投票
要详细说明之前lorem ipsum的答案,你可以使用这个方便的

struct

struct IntDoubleBinding { let intValue : Binding<Int> let doubleValue : Binding<Double> init(_ intValue : Binding<Int>) { self.intValue = intValue self.doubleValue = Binding<Double>(get: { return Double(intValue.wrappedValue) }, set: { intValue.wrappedValue = Int($0) }) } }
然后在你的代码中,像这样使用它:

struct IntSlider: View { @State var score:Int = 0 var body: some View { VStack{ Text(score.description) Slider(value: IntDoubleBinding($score).doubleValue, in: 0.0...10.0, step: 1.0) } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.