Was trying to put together a View as in show below but getting a No exact matches in call to initializer
error on the TextField
line. I suspect it’s due to the format argument but couldn’t figure out how to fix it. The Xcode hints aren’t very helpful either (see the image below). and clicking on Show shows nothing.
Same error if I remove the where
clause (which I had added in an attempt to fix it).
I suspect the error is because the compiler’s inability to figure out the format
argument which has come requirements that cannot be seen/inferred/etc.? Documentation here.
Any suggestions how to fix that/what’s the right way to do it?
import SwiftUI
struct SliderView<T>: View where T: FloatingPoint {
let key: String
@Binding var boundValue: T
var body: some View {
ZStack {
Text(key)
.opacity(0)
TextField(key, value: $boundValue, format: .number)
}.background(.teal)
}
}
struct MyView: View {
@State private var x: Double = 0
var body: some View {
Group {
Text("x is (x, specifier: "%.2f")")
SliderView(key: "Key Text", boundValue: $x)
}.padding()
}
}
#Preview {
MyView()
}
The .number
format style you want to use is declared in FloatingPointFormatStyle<Value>
, which requires Value
to be a BinaryFloatingPoint
.
So you should write where T: BinaryFloatingPoint
, not where T: FloatingPoint
.
However, there are only 3 declarations of number
. One for FloatingPointFormatStyle<Double>
, one for FloatingPointFormatStyle<Float>
and one for FloatingPointFormatStyle<Float16>
. So you cannot use .number
in this generic way.
What you can do is to directly create a FloatingPointFormatStyle
:
TextField(key, value: $boundValue, format: FloatingPointFormatStyle<T>())
I don’t think the formatting behaviour of a format style created the sway is the same as .number
for Double
/Float
/Float16
though. I suggest adding other modifiers (e.g. .precision(.significantDigits(1...6))
) to make sure the number is formatted in a desired way.
As Sweeper explained in another answer, the generic type needs to be BinaryFloatingPoint
, not FloatingPoint
.
To get around the problem that the various FloatingPointFormatStyle
constants named number
are defined for specific types and not in generic form, you could add some initializers for the corresponding types:
struct SliderView<T>: View where T: BinaryFloatingPoint {
private let key: String
@Binding private var boundValue: T
private let format: FloatingPointFormatStyle<T>
init(key: String, boundValue: Binding<T>) where T == Double {
self.init(key: key, boundValue: boundValue, format: .number)
}
init(key: String, boundValue: Binding<T>) where T == Float {
self.init(key: key, boundValue: boundValue, format: .number)
}
init(key: String, boundValue: Binding<T>) where T == Float16 {
self.init(key: key, boundValue: boundValue, format: .number)
}
private init(key: String, boundValue: Binding<T>, format: FloatingPointFormatStyle<T>) {
self.key = key
self._boundValue = boundValue
self.format = format
}
var body: some View {
ZStack {
Text(key)
.opacity(0)
TextField(key, value: $boundValue, format: format)
}.background(.teal)
}
}