iosswiftuitextfieldresignfirstresponderbecomefirstresponder

Prevent UITextField from taking values on changing the firstResponder


I have four UITextFields, each of which represents a single digit of an OTP. I want the control to shift to consecutive textfield as the user types in the code. My implementation is below.

extension FillSignUpCodeController: UITextFieldDelegate {

    func textFieldDidBeginEditing(_ textField: UITextField) {
        textField.text = ""
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let inputString = (textField.text! as NSString).replacingCharacters(in: range, with: string)

        if inputString.count == 1 {
            switch textField {

            case textFieldCodeFirstDigit:
                textFieldCodeFirstDigit.text = inputString
                textFieldCodeFirstDigit.resignFirstResponder()
                textFieldCodeSecondDigit.becomeFirstResponder()

            case textFieldCodeSecondDigit:
                textFieldCodeSecondDigit.text = inputString
                textFieldCodeSecondDigit.resignFirstResponder()
                textFieldCodeThirdDigit.becomeFirstResponder()

            case textFieldCodeThirdDigit:
                textFieldCodeThirdDigit.text = inputString
                textFieldCodeThirdDigit.resignFirstResponder()
                textFieldCodeFourthDigit.becomeFirstResponder()

            case textFieldCodeFourthDigit:
                textFieldCodeFourthDigit.text = inputString
                textFieldCodeFourthDigit.resignFirstResponder()

            default:
                return false
            }
        }
        return true
    }
}

With this piece of code, as the user types the first digit, the first textfield takes the input value and moves the control to the next textfield. However, the second text field is taking the value of the first digit. I tried setting the text to empty after changing the firstResponder but it did not work. How can I fix this issue? Thanks.


Solution

  • Since textField(_:shouldChangeCharactersIn:replacementString:) executes before the text is present in the text field, you should put the code inside a method that is called when the text did already change.

    You should observe the changes of your text field and execute the responder-changing code there. You can find more information about that in this question.

    Moreover, resigning the first responder before changing it is redundant, you don't need to do that.

    It is also very redundant to handle every text field separately. I'd recommend including your text fields in an array and iterating through them.