Allowing single digit in UITextField in iOS

2019-01-14 04:02发布

I have a Verification ViewController, I get 4 digit verification code by SMS and I need to enter those code to login, I have created the ViewController like this

As you can see four UITextFields, I need to allow only single digit for each UITextField,

What I tried: I was trying to use shouldChangeCharactersInRange:method: , but its not getting called, I don't know what's wrong, I think because UITextFields are in UITableView so it is not working.

13条回答
甜甜的少女心
2楼-- · 2019-01-14 04:32

Try this sample tutorial passcode lock

ViewController.h

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController<UITextFieldDelegate>
{
 IBOutlet UITextField *txtPassword;
}
 @end

ViewController.m

- (void)viewDidLoad
{
[super viewDidLoad];
txtPassword.delegate=self;
}


- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
{
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 1) ? NO : YES;
}
查看更多
手持菜刀,她持情操
3楼-- · 2019-01-14 04:33

You can change the text field like this by using the delegate function of the text field. Initially, you need to set the delegate and the tag of each text field.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if ((textField.text.length >= 1) && (string.length > 0))
    {
        NSInteger nextTag = textField.tag + 1;
        // Try to find next responder
        UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
        if (! nextResponder)
            nextResponder = [textField.superview viewWithTag:1];

        if (nextResponder)
           // Found next responder, so set it.
           [nextResponder becomeFirstResponder];

        return NO;
    }
    return YES;
}

Swift 2

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    // On inputing value to textfield
    if (textField.text?.characters.count < 1  && string.characters.count > 0){
        let nextTag = textField.tag + 1;

        // get next responder
        var nextResponder = textField.superview?.viewWithTag(nextTag);

        if (nextResponder == nil){
            nextResponder = textField.superview?.viewWithTag(1);
        }
        textField.text = string;
        nextResponder?.becomeFirstResponder();
        return false;
    }
    else if (textField.text?.characters.count >= 1  && string.characters.count == 0){
        // on deleting value from Textfield
        let previousTag = textField.tag - 1;

        // get next responder
        var previousResponder = textField.superview?.viewWithTag(previousTag);

        if (previousResponder == nil){
            previousResponder = textField.superview?.viewWithTag(1);
        }
        textField.text = "";
        previousResponder?.becomeFirstResponder();
        return false;
    }
    return true;
}

Swift 4

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

    if textField.text!.count < 1  && string.count > 0{
        let nextTag = textField.tag + 1

        // get next responder
        var nextResponder = textField.superview?.viewWithTag(nextTag)

        if (nextResponder == nil){

            nextResponder = textField.superview?.viewWithTag(1)
        }
        textField.text = string
        nextResponder?.becomeFirstResponder()
        return false
    }
    else if textField.text!.count >= 1  && string.count == 0{
        // on deleting value from Textfield
        let previousTag = textField.tag - 1

        // get next responder
        var previousResponder = textField.superview?.viewWithTag(previousTag)

        if (previousResponder == nil){
            previousResponder = textField.superview?.viewWithTag(1)
        }
        textField.text = ""
        previousResponder?.becomeFirstResponder()
        return false
    }
    return true

}
查看更多
混吃等死
4楼-- · 2019-01-14 04:34

I have taken one Hidden text field & four imageViews for that with two images. One for Blank and other for Bullet same as iOS default.

Also set tags for four imageviews.

On Load set Focus for Pin Code

- (void)startPinCode
{
    txtPinCodeLockDigits.text = @"";

    for (int i = 1; i <= 4; i++) {

       UIImageView *img = (UIImageView *)[self.view viewWithTag:i];
       [img setImage:[UIImage imageNamed:@"Img_BG_PinCode.png"]];
    }

    [txtPinCodeLockDigits becomeFirstResponder];
}

Then change imageview's images as per user input and only allow four characters

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

    for (int i = 1; i <= 4; i++) {

       UIImageView *img = (UIImageView *)[self.view viewWithTag:i];
       if (i <= [result length])
           [img setImage:[UIImage imageNamed:@"Img_BG_PinCode_Filled.png"]];
       else
           [img setImage:[UIImage imageNamed:@"Img_BG_PinCode.png"]];
     }

     NSLog(@"Result :: %@", result);

     if ([result length] == 4) {
        [self performSelector:@selector(keyGenerationForApplication:) withObject:result afterDelay:0.2];
     }

     return NO;
}

After Four characters call function for generated PIN Code and store it in User Defaults same as iOS default PIN settings

- (void)keyGenerationForApplication:(NSString *)pinCode
{
     int appCode = [pinCode intValue];
     [DefaultsValues setIntegerValueToUserDefaults:appCode ForKey:PIN_LOCK_PATTERN];
}

Here, you can again call StartPinCode method for re-confirming code.

Hopefully, it'll help you.
Thanks

查看更多
地球回转人心会变
5楼-- · 2019-01-14 04:35

Modified Anurag Soni's answer in Swift 3.

  • It assumes you have outlet collection named textFields and the text fields have ordered tags set
  • It adds case when there's already some digit in text field and when user types something new - the digit is replaced
  • Input is restricted to digits only
  • It prevents from pasting more than one digit

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // Restrict to only digits
        let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
        let compSepByCharInSet = string.components(separatedBy: aSet)
        let numberFiltered = compSepByCharInSet.joined(separator: "")
    
        if string != numberFiltered {
            return false
        }
    
        // Get the unwrapped text
        guard let text = textField.text else {
            return false
        }
    
        if (text.characters.count < 1  && string.characters.count == 1) {
            // New value to empty text field
            textField.text = string
    
            // Next responder
            if let someTextField = (textFields.filter { $0.tag == textField.tag + 1 }).first {
                someTextField.becomeFirstResponder()
            } else {
                view.endEditing(true)
            }
    
            return false
        } else if (text.characters.count >= 1  && string.characters.count == 0){
            // On deleting value from Textfield
            textField.text = ""
    
            // Previous responder
            if let someTextField = (textFields.filter { $0.tag == textField.tag - 1 }).first {
                someTextField.becomeFirstResponder()
            } else {
                view.endEditing(true)
            }
    
            return false
        } else if string.characters.count == 1 {
            // There's already some digit in text field
            // Replace it with new one
            textField.text = string
    
            // Next responder
            if let someTextField = (textFields.filter { $0.tag == textField.tag + 1 }).first {
                someTextField.becomeFirstResponder()
            } else {
                view.endEditing(true)
            }
        }
    
        return false
    }
    
查看更多
神经病院院长
6楼-- · 2019-01-14 04:35

try this : - For swift 3.0

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // On inputing value to textfield
    if ((textField.text?.characters.count)! < 1  && string.characters.count > 0){
        let nextTag = textField.tag + 1;

        // get next responder
        let nextResponder = textField.superview?.viewWithTag(nextTag);
        textField.text = string;

        if (nextResponder == nil){
            textField.resignFirstResponder()
        }
        nextResponder?.becomeFirstResponder();
        return false;
    }
    else if ((textField.text?.characters.count)! >= 1  && string.characters.count == 0){
        // on deleting value from Textfield
        let previousTag = textField.tag - 1;

        // get next responder
        var previousResponder = textField.superview?.viewWithTag(previousTag);

        if (previousResponder == nil){
            previousResponder = textField.superview?.viewWithTag(1);
        }
        textField.text = "";
        previousResponder?.becomeFirstResponder();
        return false;
    }
    return true;
}

converted Anurag Soni answer in swift 3.0

You just have to implement this method only.

查看更多
狗以群分
7楼-- · 2019-01-14 04:36

Just use TextFieldDelegate method and check the length of the TextField after every changes

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let newString = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
    if newString.characters.count == 1
    {
        nextTextField.becomeFirstResponder()
        return true
    }
    else
    {
        return false
    }
}
查看更多
登录 后发表回答