Deepakraj Murugesan
Deepakraj Murugesan

Reputation: 1227

UITextField: Password Implementation

I have a OTP Screen, where I have to enter the OTP in 4 digit password in 4 Diff TextFields, The scenario is like this :

enter image description here

  1. A max character limit for each textfield is 1 and when the user enters a character in textfield it should move to next TextField.
  2. When the user clicks on back space, it should take back to previous textfield inorder to make the changes.

I have managed the working up to 70% but back space works only when the user enters all the textField. I'm pasting out my code.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    // This allows numeric text only, but also backspace for deletes
    if (string.length > 0 && ![[NSScanner scannerWithString:string] scanInt:NULL])
        return NO;

    NSUInteger oldLength = [textField.text length];
    NSUInteger replacementLength = [string length];
    NSUInteger rangeLength = range.length;

    NSUInteger newLength = oldLength - rangeLength + replacementLength;

    // This 'tabs' to next field when entering digits
    if (newLength == 1) {
        if (textField == _pin1)
        {
            [self performSelector:@selector(setNextResponder:) withObject:_pin2 afterDelay:0];
        }
        else if (textField ==_pin2)
        {
            [self performSelector:@selector(setNextResponder:) withObject:_pin3 afterDelay:0];
        }
        else if (textField == _pin3)
        {
            [self performSelector:@selector(setNextResponder:) withObject:_pin4 afterDelay:0];
        }
    }
    //this goes to previous field as you backspace through them, so you don't have to tap into them individually
    else if (oldLength > 0 && newLength == 0) {
        if (textField ==_pin4)
        {
            [self performSelector:@selector(setNextResponder:) withObject:_pin3 afterDelay:0];
        }
        else if (textField == _pin3)
        {
            [self performSelector:@selector(setNextResponder:) withObject:_pin2 afterDelay:0];
        }
        else if (textField == _pin2)
        {
            [self performSelector:@selector(setNextResponder:) withObject:_pin1 afterDelay:0];
        }
    }        
    return newLength <= 1;
}

- (void)setNextResponder:(UITextField *)nextResponder
{
    [nextResponder becomeFirstResponder];
}

working:

enter image description here

Upvotes: 4

Views: 2121

Answers (3)

gamal
gamal

Reputation: 1665

For Swift 3.x

class CodeConfirmController:ViewController,UITextFieldDelegate{

@IBOutlet weak var digitNum1: UITextField!
    @IBOutlet weak var digitNum2: UITextField!
    @IBOutlet weak var digitNum3: UITextField!
    @IBOutlet weak var digitNum4: UITextField!

 override func viewDidLoad() {
        digitNum1.delegate = self
        digitNum2.delegate = self
        digitNum3.delegate = self
        digitNum4.delegate = self
    }

    func keyboardInputShouldDelete(_ textField: UITextField) -> Bool {
        let shouldDelete: Bool = true
        if textField.text?.characters.count == 0 && (textField.text == "") {
            let tagValue: Int = textField.tag - 1
            let txtField: UITextField? = (view.viewWithTag(tagValue) as? UITextField)
            txtField?.becomeFirstResponder()
        }
        return shouldDelete
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // This allows numeric text only, but also backspace for deletes
        if (string.characters.count ) > 0 && !Scanner(string: string).scanInt32(nil) {
            return false
        }
        if textField.text?.characters.count == 0 {
            perform(#selector(self.changeTextFieldFocus(toNextTextField:)), with: textField, afterDelay: 0.1)
        }
        return true
    }

    func changeTextFieldFocus(toNextTextField textField: UITextField) {
        let tagValue: Int = textField.tag + 1
        let txtField: UITextField? = (view.viewWithTag(tagValue) as? UITextField)
        txtField?.becomeFirstResponder()
    }
}

Upvotes: 0

Nimit Parekh
Nimit Parekh

Reputation: 16864

Need to give tag into UITextView in StoryBoard.

Like textField1 = 101,textField2 = 102,textField3 = 103,textField4 = 104

enter image description here

Put following code into your viewcontroller.

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
    BOOL shouldDelete = YES;

    if ([textField.text length] == 0 && [textField.text isEqualToString:@""]) {
        long tagValue = textField.tag - 1;
        UITextField *txtField = (UITextField*) [self.view viewWithTag:tagValue];

        [txtField becomeFirstResponder];
    }
    return shouldDelete;
}

Following code for the next UItextField Focus.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    // This allows numeric text only, but also backspace for deletes
    if (string.length > 0 && ![[NSScanner scannerWithString:string] scanInt:NULL])
        return NO;

    if ([textField.text length] == 0) {
        [self performSelector:@selector(changeTextFieldFocusToNextTextField:) withObject:textField afterDelay:0.3];
    }

    return YES;
}

-(void)changeTextFieldFocusToNextTextField:(UITextField*)textField{
    long tagValue = textField.tag + 1;
    UITextField *txtField = (UITextField*) [self.view viewWithTag:tagValue];
    [txtField becomeFirstResponder];
}

Sample Output

enter image description here

Grab the solution demo project

Upvotes: 7

Surya Subenthiran
Surya Subenthiran

Reputation: 2217

The reason is when UITextField is empty it won't detect the backspace event.

So to detect backspace event in this situation, you have to Subclass the UITextField and override the deleteBackward method.

Verify the following link for more details.

Detect backspace in UITextField

EDIT: Code added.

-Subclass the all the 4 UITextfield with following class.

@protocol MyTextFieldDelegate <NSObject>
@optional
- (void)textFieldDidDelete:(UITextField *)textField;
@end

@interface MyTextField : UITextField<UIKeyInput>

@property (nonatomic, assign) id<MyTextFieldDelegate> myDelegate;
@end

@implementation MyTextField

- (void)deleteBackward {
    [super deleteBackward];

    if ([_myDelegate respondsToSelector:@selector(textFieldDidDelete:)]){
        [_myDelegate textFieldDidDelete:self];
    }
}

@end

-Set delegate to self for all UITextFields and adopt MyTextFieldDelegate protocol in the class

  _pin1.myDelegate=self;
  _pin2.myDelegate=self;
  _pin3.myDelegate=self;
  _pin4.myDelegate=self;

-Now implement textFieldDidDelete method as follows

- (void)textFieldDidDelete:(UITextField *)textField {
    if (textField ==_pin4)
    {
        [self performSelector:@selector(setNextResponder:) withObject:_pin3 afterDelay:0];
    }
    else if (textField == _pin3)
    {
        [self performSelector:@selector(setNextResponder:) withObject:_pin2 afterDelay:0];
    }
    else if (textField == _pin2)
    {
        [self performSelector:@selector(setNextResponder:) withObject:_pin1 afterDelay:0];
    }
}

Hope this helps.

Upvotes: 2

Related Questions