4

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

Deepakraj Murugesan
  • 1,195
  • 11
  • 30

3 Answers3

7

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

Nimit Parekh
  • 16,776
  • 8
  • 50
  • 72
2

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.

Community
  • 1
  • 1
Surya Subenthiran
  • 2,217
  • 1
  • 15
  • 22
  • @SuryaSubenthiran have you tried this code posted in your answer. it's not detect when `UITextField` is empty. – Nimit Parekh Apr 27 '16 at 14:39
  • @NimitParekh. I tried. it works fine. deleteBackward method called even if textfield is empty. – Surya Subenthiran Apr 27 '16 at 14:46
  • @SuryaSubenthiran here problem not for next `UITextField` but issue is for previous `UITextField`. – Nimit Parekh Apr 27 '16 at 14:55
  • 1
    @NimitParekh. If you look at the gif clearly you can find the problem. The backspace works for the final text field because it contains text. but other text fields it's empty. For empty text field if you press backspace it won't call shouldChangeCharactersInRange method. – Surya Subenthiran Apr 27 '16 at 15:00
0

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()
    }
}
gamal
  • 1,587
  • 2
  • 21
  • 43