Home > Blockchain >  How to remove special characters from the `TextField` in SwiftUI
How to remove special characters from the `TextField` in SwiftUI

Time:02-11

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
        }
}
  • Related