I have a view with with an Int property named "score" that I want to adjust with a slider.
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)
}
}
}
But SwiftUI's Slider only works with doubles/floats.
How can I make it work with my integer?
As an alternative to the other answers on this page, I propose .convert
methods implemented as type-constrained, generic extension methods on Binding
which automatically do the conversion for you inline. Using your code above, here's all you have to do:
Slider(value: .convert($score), in: 0.0...10.0, step: 1.0)
It automatically converts any Int
type (e.g. Int
, Int8
, Int64
) to any Float
type (e.g. Float
, Float16
, CGFloat
, Double
), (and vice versa should you need it) thanks to generics/overloads.
Once you have added the extension to your project (or in a package referenced by your project), you simply call .convert
at any binding site and it 'just works.'
There is nothing else needed to use it (i.e. no 'proxy' structs, vars or other local items to clutter your view.) You simply use the method directly inline as shown above.
When in any place that takes a Binding
, simply type .
and code-completion will automatically suggest convert
as an option. This works because each extension method is both defined on Binding
, and it returns a Binding
(i.e. Self
) as its result type, thus making it discoverable to the auto-complete system. (This is true for any such static methods on any type.)
Here are the aforementioned extension methods...
public extension Binding {
static func convert<TInt, TFloat>(_ 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>(_ floatBinding: Binding<TFloat>) -> Binding<TInt>
where TFloat: BinaryFloatingPoint,
TInt: BinaryInteger {
Binding<TInt> (
get: { TInt(floatBinding.wrappedValue) },
set: { floatBinding.wrappedValue = TFloat($0) }
)
}
}
...and here's a playground-ready demonstration showing them in use.
(Note: Don't forget to also copy over the extension methods above!)
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($count), in: 1...8, step: 1)
}
}
.padding()
}
}
And finally, here are the results...