0

I added a UITextField and I want to restrict it to only alphabets and spaces. So with the following;

let set = CharacterSet (charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ")

How can I do this without using delegates if possible and if possible using a method like RxSwift, RxCocoa etc .. Does anyone have a brilliant idea?

Mehmet Ceylan
  • 385
  • 1
  • 6
  • 18
  • Not reactive, but also doesn't use delegates: https://medium.com/swift2go/a-better-approach-to-text-field-validations-on-ios-81bd87598070 – koen Aug 24 '20 at 14:07

3 Answers3

1

You can check like this:

func validateField(enteredString:String) -> Bool {
    
    let validationFormat = "[a-zA-Z\\s]+"
    let fieldPredicate = NSPredicate(format:"SELF MATCHES %@", validationFormat)
    return fieldPredicate.evaluate(with: enteredString)
}

And use

if !validateField(enteredString: textField.text ?? "") {
            
    print("Invalid String")
    return false
}
Kishan Bhatiya
  • 2,175
  • 8
  • 14
0

You can add an event to the textfield

textField1.addTarget(self, action: #selector(YourViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)

Handle the text when it gets changed

func textFieldDidChange(textField: UITextField) {
    // Check the textField text and update textField
}
Vibhor
  • 70
  • 9
0

You can find a pretty good implementation of Rx text filtering here: RxSwift replacement shouldChangeCharactersInRange

In your case, it should look like this:

textField.rx.text.orEmpty
    .map(alphaNumericAndWhitespace)
    .subscribe(onNext: setPreservingCursor(on: textField))
    .disposed(by: bag)

func alphaNumericAndWhitespace(_ text: String) -> String {
    let customSet = CharacterSet.alphanumerics.union(CharacterSet.whitespaces)
    return text.components(separatedBy: customSet.inverted).joined(separator: "")
}

func setPreservingCursor(on textField: UITextField) -> (_ newText: String) -> Void {
    return { newText in
        let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: textField.selectedTextRange!.start) + newText.count - (textField.text?.count ?? 0)
        textField.text = newText
        if let newPosition = textField.position(from: textField.beginningOfDocument, offset: cursorPosition) {
            textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
        }
    }
}
Adis
  • 4,512
  • 2
  • 33
  • 40
  • I disagree. That method is often considered hostile by the people on the receiving end, and I'm more inclined to repeat the (potentially) correct answer than to perpetuate a well established unfriendly SO practices. – Adis Aug 25 '20 at 14:24