Разрешение одной цифры в UITextField в iOS

У меня есть Verification ViewController, я получаю 4-значный код подтверждения по SMS, и мне нужно ввести этот код для входа в систему, я создал ViewController, как этот

Как вы можете видеть четыре UITextField s, мне нужно разрешить только одну цифру для каждого UITextField,

Что я пытался: я пытался использовать shouldChangeCharactersInRange:method:, но его не вызывали, я не знаю, что неправильно, я думаю, потому что UITextField находятся в UITableView, поэтому он не работает.

Ответ 1

Вы можете изменить текстовое поле, как это, используя функцию делегата текстового поля. Первоначально вам нужно установить делегат и тег для каждого текстового поля.

- (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

}

Ответ 2

Используйте этот код, если вы не хотите работать с тегом, и он работает лучше, чем выше

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){
            if(textField == txtOne)
            {
                txtTwo.becomeFirstResponder()
            }
            if(textField == txtTwo)
            {
                txtThree.becomeFirstResponder()
            }
            if(textField == txtThree)
            {
                txtFour.becomeFirstResponder()
            }

            textField.text = string
            return false
        }
        else if ((textField.text?.characters.count)! >= 1  && string.characters.count == 0){
            // on deleting value from Textfield
            if(textField == txtTwo)
            {
                txtOne.becomeFirstResponder()
            }
            if(textField == txtThree)
            {
                txtTwo.becomeFirstResponder()
            }
            if(textField == txtFour)
            {
                txtThree.becomeFirstResponder()
            }
            textField.text = ""
            return false
        }
        else if ((textField.text?.characters.count)! >= 1  )
        {
            textField.text = string
            return false
        }
        return true
    }

Ответ 3

Этого можно достичь с помощью делегата UITextField и путем установки тега для каждого текстового поля в порядке возрастания (скажем, 1 - 4), ниже - обработчик делегата для решения проблемы.

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 deleteing 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

Я взял одно скрытое текстовое поле и четыре изображения для этого с двумя изображениями. Один для пустого и другого для Bullet, как и для iOS по умолчанию.

Также установите теги для четырех изображений.

Включение нагрузки Фокус для PIN-кода

- (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];
}

Затем измените изображения изображения в соответствии с пользовательским вводом и разрешите только четыре символа

- (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;
}

После того, как четыре символа вызывают функцию для сгенерированного PIN-кода и сохраняют его в настройках пользователя по умолчанию, аналогично настройкам PIN-кода iOS

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

Здесь вы можете снова вызвать метод StartPinCode для повторного подтверждения кода.

Надеюсь, это поможет вам.
Спасибо

Ответ 5

swift 2.3

      class BankDepositsWithOTPVC: UIViewController {

            let limitLength = 1  

        override func viewDidLoad() {
            super.viewDidLoad()
        }


        }

        // MARK: Textfield Validator

        extension BankDepositsWithOTPVC : UITextFieldDelegate {


            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
                    let nextResponder = textField.superview?.viewWithTag(nextTag);

                    if (nextResponder == nil){
                        textField.resignFirstResponder()
                      //  nextResponder = textField.superview?.viewWithTag(1);
                    }
                    textField.text = string;
                    nextResponder?.becomeFirstResponder();
                    return false;

                }else  if (textField.text?.characters.count >= 1  && string.characters.count > 0){
                                    // maximum 1 digit

textField.text = "";

                    let nextTag = textField.tag + 1;

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

                    if (nextResponder == nil){
                        textField.resignFirstResponder()
                        //  nextResponder = textField.superview?.viewWithTag(1);
                    }
                    textField.text = string;
                    nextResponder?.becomeFirstResponder();
                    return false;
                }
                else if (textField.text?.characters.count >= 1  && string.characters.count == 0){
                    // on deleteing 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;

                guard let text = textField.text else { return true }
                let newLength = text.characters.count + string.characters.count - range.length
                return newLength <= limitLength

            }

        }

Objective-C

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

    if ((textField.text.length < 1) && (string.length > 0))
    {

        NSInteger nextTag = textField.tag + 1;
        UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
        if (! nextResponder){
            [textField resignFirstResponder];
        }
        textField.text = string;
        if (nextResponder)
            [nextResponder becomeFirstResponder];

        return NO;

    }else if ((textField.text.length >= 1) && (string.length > 0)){
        //FOR MAXIMUM 1 TEXT

        NSInteger nextTag = textField.tag + 1;
        UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
        if (! nextResponder){
            [textField resignFirstResponder];
        }
        textField.text = string;
        if (nextResponder)
            [nextResponder becomeFirstResponder];

        return NO;
    }
    else if ((textField.text.length >= 1) && (string.length == 0)){
        // on deleteing value from Textfield

        NSInteger prevTag = textField.tag - 1;
        // Try to find prev responder
        UIResponder* prevResponder = [textField.superview viewWithTag:prevTag];
        if (! prevResponder){
            [textField resignFirstResponder];
        }
        textField.text = string;
        if (prevResponder)
            // Found next responder, so set it.
            [prevResponder becomeFirstResponder];

        return NO;
    }

      return YES;
}

Ответ 6

Предоставить тег в текстовое поле, например 1,2,3,4, и напрямую использовать его

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


    let next:NSInteger

    if string == "" {

        next = textField.tag - 1;

    }
    else{

        next = textField.tag + 1;

    }

    if (textField.text?.characters.count)! >= 1 {

        if textField.tag == 4 {

            if string == "" {

                textField.text = ""

                let temptf = self.view.viewWithTag(next) as! UITextField

                temptf.becomeFirstResponder()

                return false

            }
            else{

                if (textField.text?.characters.count)! > 1 {

                    let stringg = textField.text!
                    textField.text = stringg.replacingOccurrences(of: stringg, with: string)
                }
                return false
            }
        }
        else{
            if string == "" {

                textField.text = ""

                if next != 0 {

                    let temptf = self.view.viewWithTag(next) as! UITextField
                    temptf.becomeFirstResponder()

                }

                return false
            }

            else{

                if (textField.text?.characters.count)! > 1 {


                    let stringg = textField.text!
                    textField.text = stringg.replacingOccurrences(of: stringg, with: string)

                }

                    let temptf = self.view.viewWithTag(next) as! UITextField

                    temptf.becomeFirstResponder()

            }
        }
    }

    return true

}

Ответ 7

Попробуйте это образец учебника блокировка паролей

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;
}

Ответ 8

Модифицированный ответ Анурага Сони в Swift 3.

  • Предполагается, что у вас есть коллекция выпусков с именем textFields, а текстовые поля имеют упорядоченные теги, установленные
  • Он добавляет случай, когда в текстовом поле уже есть какая-то цифра и когда пользователь вводит что-то новое - цифра заменяется
  • Вход ограничивается только цифрами
  • Это предотвращает вставку более чем одной цифры

    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 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
    }
    

Ответ 9

Swift 4

Вдохновленные ответами @Anurag Soni и @Varun Naharia

Вариант А

extension EnterConfirmationCodeTextField: UITextFieldDelegate {

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        guard let textFieldCount = textField.text?.count else { return false }

        // Сlosure
        let setValueAndMoveForward = {
            textField.text = string
            let nextTag = textField.tag + 1
            if let nextResponder = textField.superview?.viewWithTag(nextTag) {
                nextResponder.becomeFirstResponder()
            }
        }

        // Сlosure
        let clearValueAndMoveBack = {
            textField.text = ""
            let previousTag = textField.tag - 1
            if let previousResponder = textField.superview?.viewWithTag(previousTag) {
                previousResponder.becomeFirstResponder()
            }
        }

        if textFieldCount < 1 && string.count > 0 {

            setValueAndMoveForward()

            if textField.tag == 4 {
                print("Do something")
            }

            return false

        } else if textFieldCount >= 1 && string.count == 0 {

            clearValueAndMoveBack()
            return false

        } else if textFieldCount >= 1 && string.count > 0 {

            let nextTag = self.tag + 1
            if let previousResponder = self.superview?.viewWithTag(nextTag) {
                previousResponder.becomeFirstResponder()

                if let activeTextField = previousResponder as? UITextField {
                    activeTextField.text = string
                }
            }

            return false
        }

        return true

    }

}

Вариант Б (немного другое поведение):

extension EnterConfirmationCodeTextField: UITextFieldDelegate {

        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            guard let textFieldCount = textField.text?.count else { return false }

            // Сlosure
            let setValueAndMoveForward = {
                textField.text = string
                let nextTag = textField.tag + 1
                if let nextResponder = textField.superview?.viewWithTag(nextTag) {
                    nextResponder.becomeFirstResponder()
                }
            }

            // Сlosure
            let clearValueAndMoveBack = {
                textField.text = ""
                let previousTag = textField.tag - 1
                if let previousResponder = textField.superview?.viewWithTag(previousTag) {
                    previousResponder.becomeFirstResponder()
                }
            }

            if textFieldCount < 1 && string.count > 0 {

                setValueAndMoveForward()

                if textField.tag == 4 {
                    print("Do something")
                }

                return false

            } else if textFieldCount >= 1 && string.count == 0 {

                clearValueAndMoveBack()
                return false

            } else if textFieldCount >= 1 {

                setValueAndMoveForward()
                return false
            }

            return true

        }

 }

Также я реализовал эту функцию:

enter image description here

В случае, когда последний textFiled пуст, я просто хочу переключиться на предыдущий textFiled. Я перепробовал все эти методы. Но, как по мне, метод ниже более элегантный и работает как шарм:

Вариант А

class EnterConfirmationCodeTextField: UITextField {

    // MARK: Life cycle

    override func awakeFromNib() {
        super.awakeFromNib()

        delegate = self
    }

    // MARK: Methods

    override func deleteBackward() {
        super.deleteBackward()

        let previousTag = self.tag - 1
        if let previousResponder = self.superview?.viewWithTag(previousTag) {
            previousResponder.becomeFirstResponder()

            if let activeTextField = previousResponder as? UITextField {
                if let isEmpty = activeTextField.text?.isEmpty, !isEmpty {
                    activeTextField.text = String()
                }
            }
        }
    }

}

Вариант Б (немного другое поведение):

class EnterConfirmationCodeTextField: UITextField {

    // MARK: Life cycle

    override func awakeFromNib() {
        super.awakeFromNib()

        delegate = self
    }

    // MARK: Methods

    override func deleteBackward() {
        super.deleteBackward()

        let previousTag = self.tag - 1
        if let previousResponder = self.superview?.viewWithTag(previousTag) {
            previousResponder.becomeFirstResponder()
        }
    }

}

Назначьте EnterConfirmationCodeTextField для каждого из ваших textFields и установите для них соответствующее значение tag.

Ответ 10

enter image description here

Swift 4

когда вы вводите число из пин-кода в текстовое поле, вы должны позволить отображать число, и тогда следующее текстовое поле станет первым респондентом, поэтому измените первый респондент после того, как код был в текстовом представлении

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard let textField = textField as? PinCodeTextField else { return true }

    if string == "" {// when the backward clicked, let it go :)
        return true
    }

    // when textfield is not empty, well, next number
    if textField.pinCode.count == textField.maxCount {
        becomeFirstResponder(after: textField)
        return false
    }

    if string.count > textField.maxCharacterCount {// the max character count should be 1
        return false
    }
    return true
}
// now the text field has been filled with a number
func textFieldCotentDidChange(_ textField: UITextField) {
    print("didchange")
    guard let textField = textField as? PinCodeTextField else { return }
    if textField.pinCode.count == 0 {
        becomeFirstResponder(before: textField)
    }

    // when textfield has been filled, ok! next!
    if textField.pinCode.count == textField.maxCharacterCount {
        becomeFirstResponder(after: textField)
    }
}

для более подробной информации и простой демонстрации, смотрите эту ссылку

Ответ 11

Просто используйте метод TextFieldDelegate и проверьте длину TextField после каждого изменения

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
    }
}

Ответ 12

Я работал над похожим функционалом и делал это по-своему. Решение ниже. Swift 4

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

 //For clear button pressed
 //If the textfield has already text in it
 if string.count == 0 {
    textField.text = string
    return true
 }

 //For First time entry into the text field
 guard let text = textField.text, text.count <= 0  else {
    //If user enter second character
    return false
 }
 //For First time entry into the text field
 if text.count == 0  {
     textField.text = string
     textField.resignFirstResponder()
     self.nextResponde(tag: textField.tag)
     return true
  }
  return false
}

//To make the next field as responder
func nextResponde(tag: Int) {
    switch tag {
    case self.PINTextField.tag:
        guard let text = self.PINTextField1.text, text.count == 1 else {
            self.PINTextField1.becomeFirstResponder()
            return
        }
    case self.PINTextField1.tag:
        guard let text = self.PINTextField2.text, text.count == 1 else {
            self.PINTextField2.becomeFirstResponder()
            return
        }
    case self.PINTextField2.tag:
        guard let text = self.PINTextField3.text, text.count == 1 else {
            self.PINTextField3.becomeFirstResponder()
            return
        }
    default:
        let _ = tag
    }
}

Ответ 13

попробуйте следующее: - Для быстрого 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;
}

преобразовал ответ Anurag Soni в swift 3.0

Вам просто нужно реализовать этот метод.