I need to apply some validations in my form like remove special character, accept only number, alphabets, alphanumeric, and only specific length of a string.
I have many text fields, in many places in my app. So that I'm creating extensions to Binding, and trying to apply conditions when editing.
These filters/conditions using for @State
, @Published
, and @Binding
variables.
Here is my code:
struct ContentView: View {
@State var name = ""
var body: some View {
InputField(text: $name)
}
}
struct InputField: View {
@Binding var text: String
var body: some View {
VStack {
TextField("Name here...", text: $text.limit(6).alphaNumeric)
.frame(maxWidth: .infinity, minHeight: 48, alignment: .leading)
}.padding()
}
}
extension Binding where Value == String {
var alphaNumeric: Binding<String> {
Binding<String>(get: { self.wrappedValue },
set: {
self.wrappedValue = $0.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)})
}
func limit(_ length: Int) -> Binding<String> {
Binding<String>(get: { self.wrappedValue },
set: {
print($0.prefix(length))
self.wrappedValue = String($0.prefix(length))
})
}
}
Here in the above code $text.limit(6).alphaNumeric
, I'm trying to limit the length to 6 characters and only allow the alphaNumeric string.
CodePudding user response:
How about a custom TextField with checks, instead of messing with Binding Wrapper itself: (with OPs full code to proof that binding works)
struct ContentView: View {
@State var name = ""
@State var number = ""
var body: some View {
InputField(text: $name, number: $number)
}
}
struct InputField: View {
@Binding var text: String
@Binding var number: String
var body: some View {
VStack {
TextFieldWithCheck("Name here...", text: $text, limit: 15, allowed: .alphanumerics)
.frame(maxWidth: .infinity, minHeight: 48, alignment: .leading)
TextFieldWithCheck("Phone no here...", text: $number, limit: 9, allowed: .decimalDigits)
.frame(maxWidth: .infinity, minHeight: 48, alignment: .leading)
}.padding()
}
}
// here is the custom TextField itself
struct TextFieldWithCheck: View {
let label: LocalizedStringKey
@Binding var text: String
let limit: Int
let allowed: CharacterSet
init(_ label: LocalizedStringKey, text: Binding<String>, limit: Int = Int.max, allowed: CharacterSet = .alphanumerics) {
self.label = label
self._text = Binding(projectedValue: text)
self.limit = limit
self.allowed = allowed
}
var body: some View {
TextField(label, text: $text)
.onChange(of: text) { _ in
// all credits to Leo Dabus:
text = String(text.prefix(limit).unicodeScalars.filter(allowed.contains))
}
}
}
CodePudding user response:
You can use .onChange(of: )
. Should also work for @Binding etc.
@State private var inputText = ""
let maxCount = 20
var body: some View {
TextField("Input", text: $inputText)
.padding()
.background(.gray.opacity(0.1))
.padding()
.onChange(of: inputText) { _ in
// check character set
var checked = inputText.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
// check length
checked = String(checked.prefix(maxCount)) // regards to Leo Dabus
inputText = checked
}
}