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:13

I was working on a similar functionality and did it in my way. Solution below. 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
    }
}
查看更多
放荡不羁爱自由
3楼-- · 2019-01-14 04:17

It can be achieve using UITextField delegate & by setting Tag for each Textfield in increasing order (say 1 - 4), below is the delegate handler to solve the issue.

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;
    }
查看更多
Explosion°爆炸
4楼-- · 2019-01-14 04:18

Swift 4

Inspired by @Anurag Soni and @Varun Naharia answers

Variant A

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

    }

}

Variant B (a little bit another behavior):

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

        }

 }

Also, I implemented this feature:

enter image description here

In the case where the last textFiled is empty, I just want to switch to the previous textFiled. I tried all this methods. But as for me the method below more elegant and works like a charm:

Variant A

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

}

Variant B (a little bit another behavior):

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

}

Assign EnterConfirmationCodeTextField for each of your textFields and set they appropriate tag value.

查看更多
Emotional °昔
5楼-- · 2019-01-14 04:23

Provide the tag to the textfield like 1,2,3,4 and directly use it

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

}
查看更多
疯言疯语
6楼-- · 2019-01-14 04:25

use this code if you don't want work with tag and it works better then above

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
    }
查看更多
We Are One
7楼-- · 2019-01-14 04:29

enter image description here

Swift 4

when you input a number from the pin code into a text field, you should let a number be shown and then the next text field will become the first responder, so change the first responder after the code was in the text view

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

for more details and the simple demo, see this link

查看更多
登录 后发表回答