如何创建 OTP 验证屏幕并在多个 uitextfield 上检测向后删除是 Swift

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

所以我制作了这个 otp 屏幕,但我有一些收获,

我用一堆 uitextfield 制作了这个 otp 屏幕,我制作了它的逻辑,但我无法删除我制作的文本字段中的 num

当我像数字的前两个一样填充时,文本字段不会删除,即使我按下后退按钮它也不会工作.....但是当我填充整个文本字段时它会工作,在我的例子中是六个.

所以我必须填写所有六个数字,我可以从文本字段中删除数字,如果文本字段只填写一半,它将无法工作。

这是我的代码:

 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
}

这就是我用来制作 otp uitextField 逻辑的代码......请告诉我,我知道我的逻辑有问题,谢谢。

根据制造商的说法,他说要解决这个问题我只需要“将文本字段的用户交互设置为 false 并使第一个文本字段成为第一响应者”,我想我只是这样做了但我可能做错了....

我真的需要解决这个问题,谢谢。

我更愿意创建一个自定义文本字段,而不是修复该代码,以便在按下 deleteBackward 键时发出通知。所以首先子类化 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)
    }
}

现在在你的 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 {
            [=11=]?.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