How to create OTP verification screen and detect delete backward on multiple uitextfield is Swift

混江龙づ霸主 提交于 2021-01-04 15:42:04

问题


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.

  • i watch a tutorial to make this otp screen in this vid https://www.youtube.com/watch?v=gZnBXh0TRO8

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.


回答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



来源:https://stackoverflow.com/questions/63002801/how-to-create-otp-verification-screen-and-detect-delete-backward-on-multiple-uit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!