2

so i make this otp screen but i have some catch,

i make this otp screen with bunch of uitextfield and i make the logic of it, but i just cant delete on of the num in the textfield that i make

the textfield wont delete when i fill like the first 2 of my num, even i pressess backButton it wont work.....but it will work when i fill the whole num of textfield, in my case is six.

so i have to fill all six of the number and i can delete the number from the textfield, it wont work if only half fill in the textfield.

heres my code :

 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
    if ((textField.text?.count)! < 1) && (string.count > 0) {
        if textField == txtOTP1 {
            txtOTP2.becomeFirstResponder()
        }
        if textField == txtOTP2 {
            txtOTP3.becomeFirstResponder()
        }
        if textField == txtOTP3 {
            txtOTP4.becomeFirstResponder()
        }
        if textField == txtOTP4 {
            txtOTP5.becomeFirstResponder()
        }
        if textField == txtOTP5{
            txtOTP6.becomeFirstResponder()
        }
        if textField == txtOTP6{
            txtOTP6.resignFirstResponder()
        }
        
        textField.text = string
        return false
    }else if ((textField.text?.count)! >= 1) && (string.count == 0) {
        if textField == txtOTP2{
            txtOTP1.becomeFirstResponder()
        }
        if textField == txtOTP3{
            txtOTP2.becomeFirstResponder()
        }
        if textField == txtOTP4{
            txtOTP3.becomeFirstResponder()
        }
        if textField == txtOTP5{
            txtOTP4.becomeFirstResponder()
        }
        if textField == txtOTP6{
            txtOTP5.becomeFirstResponder()
        }
        if textField == txtOTP1{
            txtOTP1.resignFirstResponder()
        }
    
        textField.text = ""
        return false
    }
    else if (textField.text?.count)! >= 1 {

        
        textField.text = string
        return false
    }
    
    return true
}

thats the code i use to make the otp uitextField logic......please tell me i know theres something wrong with my logic, thanks.

and according to the maker, he said that to fix this issue i just need to "set user interactions for textfield false and make first textfield first responder", i think i just did that but i maybe i did it wrong....

i really need to fix this guys, thanks.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
afi permana
  • 207
  • 3
  • 17

1 Answers1

1

Instead of fixing that code I prefer to create a custom text field that would inform when the deleteBackward key is pressed. So first subclass a UITextField:


import UIKit
class SingleDigitField: UITextField {
    // create a boolean property to hold the deleteBackward info
    var pressedDelete = false
    // customize the text field as you wish 
    override func willMove(toSuperview newSuperview: UIView?) {
        keyboardType = .numberPad
        textAlignment = .center
        backgroundColor = .blue
        isSecureTextEntry = true
        isUserInteractionEnabled = false
    }
    // hide cursor
    override func caretRect(for position: UITextPosition) -> CGRect { .zero }
    // hide selection
    override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] { [] }
    // disable copy paste
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { false }
    // override deleteBackward method, set the property value to true and send an action for editingChanged
    override func deleteBackward() {
        pressedDelete = true
        sendActions(for: .editingChanged)
    }
}

Now in your ViewCOntroller:

import UIKit

class ViewController: UIViewController {
    // connect the textfields outlets
    @IBOutlet weak var firstDigitField: SingleDigitField!
    @IBOutlet weak var secondDigitField: SingleDigitField!
    @IBOutlet weak var thirdDigitField: SingleDigitField!
    @IBOutlet weak var fourthDigitField: SingleDigitField!
    override func viewDidLoad() {
        super.viewDidLoad()
        // add a target for editing changed for each field
        [firstDigitField,secondDigitField,thirdDigitField,fourthDigitField].forEach {
            $0?.addTarget(self, action: #selector(editingChanged), for: .editingChanged)
        }
        // make the firsDigitField the first responder
        firstDigitField.isUserInteractionEnabled = true
        firstDigitField.becomeFirstResponder()
    }
    // here you control what happens to each change that occurs to the fields
    @objc func editingChanged(_ textField: SingleDigitField) {
        // check if the deleteBackwards key was pressed
        if textField.pressedDelete {
            // reset its state
            textField.pressedDelete = false
            // if the field has text empty its content
            if textField.hasText {
                textField.text = ""   
            } else {
                // otherwise switch the field, resign the first responder and activate the previous field and empty its contents
                switch textField {
                case secondDigitField, thirdDigitField, fourthDigitField:
                    textField.resignFirstResponder()
                    textField.isUserInteractionEnabled = false
                    switch textField {
                    case secondDigitField:
                        firstDigitField.isUserInteractionEnabled = true
                        firstDigitField.becomeFirstResponder()
                        firstDigitField.text = ""
                    case thirdDigitField:
                        secondDigitField.isUserInteractionEnabled = true
                        secondDigitField.becomeFirstResponder()
                        secondDigitField.text = ""
                    case fourthDigitField:
                        thirdDigitField.isUserInteractionEnabled = true
                        thirdDigitField.becomeFirstResponder()
                        thirdDigitField.text = ""
                    default:
                        break
                    }
                default: break
                }
            }
        }
        // make sure there is only one character and it is a number otherwise delete its contents
        guard textField.text?.count == 1, textField.text?.last?.isWholeNumber == true else {
            textField.text = ""
            return
        }
        // switch the textField, resign the first responder and make the next field active
        switch textField {
        case firstDigitField, secondDigitField, thirdDigitField:
            textField.resignFirstResponder()
            textField.isUserInteractionEnabled = false
            switch textField {
            case firstDigitField:
                secondDigitField.isUserInteractionEnabled = true
                secondDigitField.becomeFirstResponder()
            case secondDigitField:
                thirdDigitField.isUserInteractionEnabled = true
                thirdDigitField.becomeFirstResponder()
            case thirdDigitField:
                fourthDigitField.isUserInteractionEnabled = true
                fourthDigitField.becomeFirstResponder()
            default: break
            }
        case fourthDigitField:
            fourthDigitField.resignFirstResponder()
        default: break
        }
    }
}

Xcode 12 sample project

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • I am getting this error -[UIApplication getKeyboardDevicePropertiesForSenderID:shouldUpdate:usingSyntheticEvent:], failed to fetch device property for senderID (778835616971358209) use primary keyboard info instead. – Anilkumar iOS - ReactNative May 19 '22 at 11:28