How to Test Character Input to Uitextfield as the User Enters Characters and Prevent Invalid Characters

How to Test Character Input to UITextField as the User Enters Characters and Prevent Invalid Characters

The solution ultimately turned out to be fairly trivial. Unfortunately a lot of the questions and answers related to this question are about validating or formatting numeric values, not controlling what a user could input.

The following implementation of the shouldChangeCharactersInRange delegate method is my solution. As always, regular expressions rock in this situation. RegExLib.com is an excellent source for useful RegEx samples or solutions. I'm not a RegEx guru and always struggle a bit putting them together so any suggestions to improve it are welcome.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField == self.quantityTextField)
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];

NSString *expression = @"^([0-9]+)?(\\.([0-9]{1,2})?)?$";

NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:expression
options:NSRegularExpressionCaseInsensitive
error:nil];
NSUInteger numberOfMatches = [regex numberOfMatchesInString:newString
options:0
range:NSMakeRange(0, [newString length])];
if (numberOfMatches == 0)
return NO;
}

return YES;
}

The above code allows the user to input these kinds of values: 1, 1.1, 1.11, .1, .11. Notice that this implementation does not replace the text field's string, which would causes a recursion. This solution simply rejects the next character the user inputs if the 'newString' does not match the regex.

Only allow letters and numbers for a UITextField

If you really want to restrict user from entering special characters, then probably the best way is to use shouldChangeCharactersInRange: method

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField==YOUR_USER_NAME_TEXTFIELD || textField == YOUR_PASSWORD_TEXTFIELD)
{

NSCharacterSet *myCharSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"];
for (int i = 0; i < [string length]; i++)
{
unichar c = [string characterAtIndex:i];
if (![myCharSet characterIsMember:c])
{
return NO;
}
}

return YES;
}

return YES;
}

This will restrict the user from entering the special characters. As rmaddy pointed out, it is always good to do this kind of validation when data entry happens.

PS: Make sure you set the delegate for your textfields.

iOS Delegate to prevent invalid inputs

Hmm, I'm not sure why that isn't working. Why don't you use a 'Decimal Pad' keyboard type for your textfield?

If for some reason you don't want to, try this:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

switch string {
case "0","1","2","3","4","5","6","7","8","9":
return true
case ".":
if let _ = textField.text?.rangeOfString("."){
return false
}else{
return true
}
default:
if let _ = string.rangeOfCharacterFromSet(NSCharacterSet.letterCharacterSet()){
return false
}

return true
}
}

EDIT: I think I found the problem in your code, you need to separate the if statement into two conditions:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

let existingTextHasDecimalSeperator = textField.text?.rangeOfString(".")
let replacementTextHasDecimalSeparator = string.rangeOfString(".")
let alphabeticString = string.rangeOfCharacterFromSet(NSCharacterSet.letterCharacterSet())

if (existingTextHasDecimalSeperator != nil && replacementTextHasDecimalSeparator != nil) || alphabeticString != nil {
return false
}
else {
return true
}
}

Make UITextfield accept only Katakana characters - swift

From my observations, markedTextRange doesn't update until after shouldChangeCharactersInRange is called. Let's say I enter an s, shouldChangeCharactersInRange is called first, then markedTextRange gets updated. This means it's really hard to distinguish between "entering the first 's' on an English keyboard" and "entering the first 's' on the Romaji keyboard", the former of which we want to disallow. In both cases, markedTextRange is nil in shouldChangeCharactersInRange.

Therefore, I think it would be easier if you do it on editingChanged instead:

textfield.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged)

...

let katakanas =
"アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン"
@objc func textFieldChanged() {
let newString = textfield.text ?? ""
if !(newString.count == 1 &&
katakanas.contains(newString.first!)) && textfield.markedTextRange == nil {
// perhaps show an error alert here too
textfield.text = ""
}
}

This gets called only after the text changes and markedTextRange gets updated, so the textfield.markedTextRange == nil check will work correctly. Setting the entire text field to blank in case the user enters something invalid shouldn't be too bad of a UX, as you only meant to accept one character anyway.

You can still use shouldChangeCharactersInRange to prevent users from entering further letters after there is already one katakana:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let firstChar = textField.text?.first,
katakanas.contains(firstChar) && string != "" {
return false
}
return true
}

Allow only alphanumeric characters for a UITextField

Use the UITextFieldDelegate method -textField:shouldChangeCharactersInRange:replacementString: with an NSCharacterSet containing the inverse of the characters you want to allow. For example:

// in -init, -initWithNibName:bundle:, or similar
NSCharacterSet *blockedCharacters = [[[NSCharacterSet alphanumericCharacterSet] invertedSet] retain];

- (BOOL)textField:(UITextField *)field shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)characters
{
return ([characters rangeOfCharacterFromSet:blockedCharacters].location == NSNotFound);
}

// in -dealloc
[blockedCharacters release];

Note that you’ll need to declare that your class implements the protocol (i.e. @interface MyClass : SomeSuperclass <UITextFieldDelegate>) and set the text field’s delegate to the instance of your class.

Not allowing user to enter the number 1 as the first digit in a UITextField

I have modified the example you want to use as per your need, you can check it out

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField==self.testTextField&&range.location==0)
{
if ([string hasPrefix:@"1"])
{
return NO;
}
}

return YES;
}

How to restrict user to type till 29 digits and take two decimals in swift?

Use textField delegate method to restrict input values, its called whenever the textfield text is updated/changed.

func textField(_ textField: UITextField, shouldChangeCharactersIn
range: NSRange, replacementString string: String) -> Bool

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
let newString = NSString(string: textField.text!).replacingCharacters(in: range, with: string)

if newString.characters.count > 32 { //restrict input upto 32 characters
return false
} else {

let characterset = CharacterSet(charactersIn: "0123456789.") //0-9 digit and . is allowed

if newString.rangeOfCharacter(from: characterset.inverted) == nil {

let fullNumberArray = newString.components(separatedBy: ".") //Convert string into array
if fullNumberArray.count > 2 { // more than 2 . exist
return false
}
else if fullNumberArray.count == 2 { // Fractional part exist
if fullNumberArray[0].characters.count <= 29 && fullNumberArray[1].characters.count <= 2 {
return true
} }else {
// Only No decimal point exist , numeric digits only entered so far
if fullNumberArray[0].characters.count <= 29 {
return true
}
}

}

}

return false
}

Note
Above code allows to enter maximum 29 numeric digits and maximum 2 fractional digits.User can type numbers without decimals .If you have a restriction of only allow 32 character and there is minimum and maximum 29 digit without fraction range then some conditions will be reduced.

Allow only Numbers for UITextField input

This is how you might handle the problem on a SSN verification field, you can modify the max length and remove the if statement checking for keyboard type if you need to.

There is also logic to suppress the max length alerts when the user is typing as opposed to pasting data.

Within the context of this code, presentAlert()/presentAlert: is just some basic function that presents a UIAlertController (or a legacy UIAlertView) using the message string passed.

Swift 5

// NOTE: This code assumes you have set the UITextField(s)'s delegate property to the 
// object that will contain this code, because otherwise it would never be called.
//
// There are also some better stylistic approaches in Swift to avoid all the
// nested statements, but I wanted to keep the styles similar to allow others
// to contrast and compare between the two languages a little easier.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

// Handle backspace/delete
guard !string.isEmpty else {

// Backspace detected, allow text change, no need to process the text any further
return true
}

// Input Validation
// Prevent invalid character input, if keyboard is numberpad
if textField.keyboardType == .numberPad {

// Check for invalid input characters
if CharacterSet(charactersIn: "0123456789").isSuperset(of: CharacterSet(charactersIn: string)) {

// Present alert so the user knows what went wrong
presentAlert("This field accepts only numeric entries.")

// Invalid characters detected, disallow text change
return false
}
}

// Length Processing
// Need to convert the NSRange to a Swift-appropriate type
if let text = textField.text, let range = Range(range, in: text) {

let proposedText = text.replacingCharacters(in: range, with: string)

// Check proposed text length does not exceed max character count
guard proposedText.count <= maxCharacters else {

// Present alert if pasting text
// easy: pasted data has a length greater than 1; who copy/pastes one character?
if string.count > 1 {

// Pasting text, present alert so the user knows what went wrong
presentAlert("Paste failed: Maximum character count exceeded.")
}

// Character count exceeded, disallow text change
return false
}

// Only enable the OK/submit button if they have entered all numbers for the last four
// of their SSN (prevents early submissions/trips to authentication server, etc)
answerButton.isEnabled = (proposedText.count == 4)
}

// Allow text change
return true
}

Objective-C

// NOTE: This code assumes you have set the UITextField(s)'s delegate property to the 
// object that will contain this code, because otherwise it would never be called.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
// Handle backspace/delete
if (!string.length)
{
// Backspace detected, allow text change, no need to process the text any further
return YES;
}

// Input Validation
// Prevent invalid character input, if keyboard is numberpad
if (textField.keyboardType == UIKeyboardTypeNumberPad)
{
if ([string rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet].invertedSet].location != NSNotFound)
{
[self presentAlert: @"This field accepts only numeric entries."];
return NO;
}
}

// Length Validation
NSString *proposedText = [textField.text stringByReplacingCharactersInRange:range withString:string];

// Check proposed text length does not exceed max character count
if (proposedText.length > maxCharacters)
{
// Present alert if pasting text
// easy: pasted data has a length greater than 1; who copy/pastes one character?
if (string.length > 1)
{
// Pasting text, present alert so the user knows what went wrong
[self presentAlert: @"Paste failed: Maximum character count exceeded."];
}

// Character count exceeded, disallow text change
return NO;
}

// Only enable the OK/submit button if they have entered all numbers for the last four
// of their SSN (prevents early submissions/trips to authentication server, etc)
self.answerButton.enabled = (proposedText.length == maxCharacters);

// Allow text change
return YES;
}


Related Topics



Leave a reply



Submit