Ios Swift-如何在每个SMS Otp UITEXT字段中输入多个字符

Ios Swift-如何在每个SMS Otp UITEXT字段中输入多个字符,ios,swift,textfield,Ios,Swift,Textfield,我正在尝试以这种格式输入用户id 为此,我试着跟随 如果我有一个字符,它工作正常,但如果你检查图像,我希望第一个文本字段显示3个字符,第二个显示4个,第三个显示7个,第四个文本字段显示1个字符。而且,若我试图从textfield中删除字符,它不应该删除所有字符,而应该一个接一个地删除字符,然后在textfield中并没有任何内容时移动到另一个字符 有人能帮我吗。提前谢谢 protocol EmiratesIdTextFieldDelegate: class { func textFie

我正在尝试以这种格式输入用户id

为此,我试着跟随

如果我有一个字符,它工作正常,但如果你检查图像,我希望第一个文本字段显示3个字符,第二个显示4个,第三个显示7个,第四个文本字段显示1个字符。而且,若我试图从textfield中删除字符,它不应该删除所有字符,而应该一个接一个地删除字符,然后在textfield中并没有任何内容时移动到另一个字符

有人能帮我吗。提前谢谢

protocol EmiratesIdTextFieldDelegate: class {
    func textFieldDidDelete()
}

import UIKit

class EmiratesIdTextField: UITextField {

weak var emiratesIdTextFieldDelegate: EmiratesIdTextFieldDelegate? // make sure to declare this as weak to prevent a memory leak/retain cycle

override func deleteBackward() {
    super.deleteBackward()
    emiratesIdTextFieldDelegate?.textFieldDidDelete()
}

// when a char is inside the textField this keeps the cursor to the right of it. If the user can get on the left side of the char and press the backspace the current char won't get deleted
override func closestPosition(to point: CGPoint) -> UITextPosition? {
    let beginning = self.beginningOfDocument
    let end = self.position(from: beginning, offset: self.text?.count ?? 0)
    return end
}

}

class PinViewController:UIViewController、EmiratesIdTextFieldDelegate、UITextFieldDelegate{
@IBOutlet弱var文本字段1:EmiratesIdTextField!
@IBOutlet弱var文本字段2:EmiratesIdTextField!
@IBOutlet弱var文本字段3:EmiratesIdTextField!
@IBOutlet弱var文本字段4:EmiratesIdTextField!
var activeTextField=UITextField()
重写func viewDidLoad(){
super.viewDidLoad()
textField1.delegate=self
textField2.delegate=self
textField3.delegate=self
textField4.delegate=self
textField1.emiratesIdTextFieldDelegate=self
textField2.emiratesIdTextFieldDelegate=self
textField3.emiratesIdTextFieldDelegate=self
textField4.emiratesIdTextFieldDelegate=self
//配置锚()
textField1.becomeFirstResponder()
}
func textField didbeginediting(textField:UITextField){
activeTextField=textField
}
func textFieldDidDelete(){
如果activeTextField==textField1{
打印(“在otpTextField1中按下了backButton”)
//无所事事
}
如果activeTextField==textField2{
打印(“在otpTextField2中按下了backButton”)
textField2.isEnabled=false
textField1.isEnabled=true
textField1.becomeFirstResponder()
textField1.text=“”
}
如果activeTextField==textField3{
打印(“在otpTextField3中按下了backButton”)
textField3.isEnabled=false
textField2.isEnabled=true
textField2.becomeFirstResponder()
textField2.text=“”
}
如果activeTextField==textField4{
打印(“在otpTextField4中按下了backButton”)
textField4.isEnabled=false
textField3.isEnabled=true
textField3.becomeFirstResponder()
textField3.text=“”
}
}
func textField(textField:UITextField,shouldChangeCharacters范围:NSRange,replacementString:string)->Bool{
让text=textField.text
如果let text=text{
//10.当用户在第一个文本字段中输入某些内容时,它将自动调整到下一个文本字段,并在此过程中执行一些禁用和启用操作。这将一直进行到最后一个文本字段
如果(text.count<1)和(string.count>0){
如果textField==textField1{
textField1.isEnabled=false
textField2.isEnabled=true
textField2.becomeFirstResponder()
}
如果textField==textField2{
textField2.isEnabled=false
textField3.isEnabled=true
textField3.becomeFirstResponder()
}
如果textField==textField3{
textField3.isEnabled=false
textField4.isEnabled=true
textField4.becomeFirstResponder()
}
如果textField==textField4{
//无所事事
}
textField.text=字符串
返回错误
}//11.如果用户到达最后一个文本字段并按下“上一步”按钮,则上面的所有内容都将反转
如果(text.count>=1)和(string.count==0),则为else{
如果textField==textField2{
textField2.isEnabled=false
textField2.isEnabled=true
textField1.becomeFirstResponder()
textField1.text=“”
}
如果textField==textField3{
textField3.isEnabled=false
textField2.isEnabled=true
textField2.becomeFirstResponder()
textField2.text=“”
}
如果textField==textField4{
textField4.isEnabled=false
textField3.isEnabled=true
textField3.becomeFirstResponder()
textField3.text=“”
}
如果textField==textField1{
//无所事事
}
textField.text=“”
返回错误
}//12.按下“后退”按钮并再次向前移动后,您必须重新执行步骤10中的操作
如果text.count>=1,则为else{
如果textField==textField1{
textField1.isEnabled=false
textField2.isEnabled=true
textField2.becomeFirstResponder()
}
如果textField==textField2{
textField2.isEnabled=false
textField3.isEnabled=true
textField3.becomeFirstResponder()
}
如果textField==textField3{
textField3.isEnabled=false
textField4.isEnabled=true
textField4.becomeFirstResponder()
}
如果textField==textField4{
//无所事事
}
textField.text=字符串
返回错误
}
}
返回真值
}
}
这类似于。您可以将其复制并粘贴到一个文件中,然后运行它以查看其工作方式

但这一条不同,因为OP想知道阿联酋航空公司如何使用每个文本字段中的多个数字来使用文本字段。我不知道它们是如何工作的,但这就是UberEats让它们的sms文本字段工作的方式,所以我将它们组合在一起,允许每个文本字段中有多个数字。你不能只是随机按一个文本字段然后选择它。使用这个你只能向前和向后移动。
class PinViewController: UIViewController,EmiratesIdTextFieldDelegate,UITextFieldDelegate {
@IBOutlet weak var textField1: EmiratesIdTextField!
@IBOutlet weak var textField2: EmiratesIdTextField!
@IBOutlet weak var textField3: EmiratesIdTextField!
@IBOutlet weak var textField4: EmiratesIdTextField!

var activeTextField = UITextField()

override func viewDidLoad() {
    super.viewDidLoad()

    textField1.delegate = self
    textField2.delegate = self
    textField3.delegate = self
    textField4.delegate = self

    textField1.emiratesIdTextFieldDelegate = self
    textField2.emiratesIdTextFieldDelegate = self
    textField3.emiratesIdTextFieldDelegate = self
    textField4.emiratesIdTextFieldDelegate = self

//  configureAnchors()

    textField1.becomeFirstResponder()
}

func textFieldDidBeginEditing(_ textField: UITextField) {

    activeTextField = textField
}

func textFieldDidDelete() {

    if activeTextField == textField1 {
        print("backButton was pressed in otpTextField1")
        // do nothing
    }

    if activeTextField == textField2 {
        print("backButton was pressed in otpTextField2")
        textField2.isEnabled = false
        textField1.isEnabled = true
        textField1.becomeFirstResponder()
        textField1.text = ""
    }

    if activeTextField == textField3 {
        print("backButton was pressed in otpTextField3")
        textField3.isEnabled = false
        textField2.isEnabled = true
        textField2.becomeFirstResponder()
        textField2.text = ""
    }

    if activeTextField == textField4 {
        print("backButton was pressed in otpTextField4")
        textField4.isEnabled = false
        textField3.isEnabled = true
        textField3.becomeFirstResponder()
        textField3.text = ""
    }
}

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let text = textField.text

    if let text = text {

        // 10. when the user enters something in the first textField it will automatically adjust to the next textField and in the process do some disabling and enabling. This will proceed until the last textField
        if (text.count < 1) && (string.count > 0) {

            if textField == textField1 {
                textField1.isEnabled = false
                textField2.isEnabled = true
                textField2.becomeFirstResponder()
            }

            if textField == textField2 {
                textField2.isEnabled = false
                textField3.isEnabled = true
                textField3.becomeFirstResponder()
            }

            if textField == textField3 {
                textField3.isEnabled = false
                textField4.isEnabled = true
                textField4.becomeFirstResponder()
            }

            if textField == textField4 {
                // do nothing
            }

            textField.text = string
            return false

        } // 11. if the user gets to the last textField and presses the back button everything above will get reversed
        else if (text.count >= 1) && (string.count == 0) {

            if textField == textField2 {
                textField2.isEnabled = false
                textField2.isEnabled = true
                textField1.becomeFirstResponder()
                textField1.text = ""
            }

            if textField == textField3 {
                textField3.isEnabled = false
                textField2.isEnabled = true
                textField2.becomeFirstResponder()
                textField2.text = ""
            }

            if textField == textField4 {
                textField4.isEnabled = false
                textField3.isEnabled = true
                textField3.becomeFirstResponder()
                textField3.text = ""
            }

            if textField == textField1 {
                // do nothing
            }

            textField.text = ""
            return false

        } // 12. after pressing the backButton and moving forward again you will have to do what's in step 10 all over again
        else if text.count >= 1 {

            if textField == textField1 {
                textField1.isEnabled = false
                textField2.isEnabled = true
                textField2.becomeFirstResponder()
            }

            if textField == textField2 {
                textField2.isEnabled = false
                textField3.isEnabled = true
                textField3.becomeFirstResponder()
            }

            if textField == textField3 {
                textField3.isEnabled = false
                textField4.isEnabled = true
                textField4.becomeFirstResponder()
            }

            if textField == textField4 {
                // do nothing
            }

            textField.text = string
            return false
        }
    }
    return true
}
}
import UIKit

protocol MyTextFieldDelegate: class {
    func textFieldDidDelete()
}

// 1. subclass UITextField and create protocol for it to know when the backButton is pressed
class MyTextField: UITextField {

    weak var myDelegate: MyTextFieldDelegate? // make sure to declare this as weak to prevent a memory leak/retain cycle

    override func deleteBackward() {
        super.deleteBackward()
        myDelegate?.textFieldDidDelete()
    }

    // when a char is inside the textField this keeps the cursor to the right of it. If the user can get on the left side of the char and press the backspace the current char won't get deleted
    override func closestPosition(to point: CGPoint) -> UITextPosition? {
        let beginning = self.beginningOfDocument
        let end = self.position(from: beginning, offset: self.text?.count ?? 0)
        return end
    }
}

// 2. set the class to use BOTH Delegates
class ViewController: UIViewController, UITextFieldDelegate, MyTextFieldDelegate {

    let staticLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 17)
        label.text = "Enter the SMS code sent to your phone"
        return label
    }()

    // 3. make each textField of type MYTextField
    let otpTextField1: MyTextField = {
        let textField = MyTextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.font = UIFont.systemFont(ofSize: 18)
        textField.autocorrectionType = .no
        textField.keyboardType = .numberPad
        textField.textAlignment = .center
        // **important this is initially ENABLED
        return textField
    }()

    let otpTextField2: MyTextField = {
        let textField = MyTextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.font = UIFont.systemFont(ofSize: 18)
        textField.autocorrectionType = .no
        textField.keyboardType = .numberPad
        textField.textAlignment = .center
        textField.isEnabled = false // **important this is initially DISABLED
        return textField
    }()

    let otpTextField3: MyTextField = {
        let textField = MyTextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.font = UIFont.systemFont(ofSize: 18)
        textField.autocorrectionType = .no
        textField.keyboardType = .numberPad
        textField.textAlignment = .center
        textField.isEnabled = false // **important this is initially DISABLED
        return textField
    }()

    let otpTextField4: MyTextField = {
        let textField = MyTextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.font = UIFont.systemFont(ofSize: 18)
        textField.autocorrectionType = .no
        textField.keyboardType = .numberPad
        textField.textAlignment = .center
        textField.isEnabled = false // **important this is initially DISABLED
        return textField
    }()

    // 4. create this property to know which textField is active. Set it in step 8 and use it in step 9
    var activeTextField = UITextField()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        // 5. set the regular UItextField delegate to each textField
        otpTextField1.delegate = self
        otpTextField2.delegate = self
        otpTextField3.delegate = self
        otpTextField4.delegate = self

        // 6. set the subClassed textField delegate to each textField
        otpTextField1.myDelegate = self
        otpTextField2.myDelegate = self
        otpTextField3.myDelegate = self
        otpTextField4.myDelegate = self

        configureAnchors()

        // 7. once the screen appears show the keyboard
        otpTextField1.becomeFirstResponder()

        // 8. add this method to each textField's target action so it can be monitored while the user is typing
        otpTextField1.addTarget(self, action: #selector(monitorTextFieldWhileTyping(_ :)), for: .editingChanged)
        otpTextField2.addTarget(self, action: #selector(monitorTextFieldWhileTyping(_ :)), for: .editingChanged)
        otpTextField3.addTarget(self, action: #selector(monitorTextFieldWhileTyping(_ :)), for: .editingChanged)
        otpTextField4.addTarget(self, action: #selector(monitorTextFieldWhileTyping(_ :)), for: .editingChanged)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        addBottomLayerTo(textField: otpTextField1)
        addBottomLayerTo(textField: otpTextField2)
        addBottomLayerTo(textField: otpTextField3)
        addBottomLayerTo(textField: otpTextField4)
    }

    // 9. when a textField is active set the activeTextField property to that textField
    func textFieldDidBeginEditing(_ textField: UITextField) {

        activeTextField = textField
    }

    // 10. when the backButton is pressed, the MyTextField delegate will get called. The activeTextField will let you know which textField the backButton was pressed in. Depending on the textField certain textFields will become enabled and disabled.
    func textFieldDidDelete() {

        if activeTextField == otpTextField1 {
            print("backButton was pressed in otpTextField1")
            // do nothing
        }

        if activeTextField == otpTextField2 {
            print("backButton was pressed in otpTextField2")
            otpTextField2.isEnabled = false
            otpTextField1.isEnabled = true
            otpTextField1.becomeFirstResponder()
        }

        if activeTextField == otpTextField3 {
            print("backButton was pressed in otpTextField3")
            otpTextField3.isEnabled = false
            otpTextField2.isEnabled = true
            otpTextField2.becomeFirstResponder()
        }

        if activeTextField == otpTextField4 {
            print("backButton was pressed in otpTextField4")
            otpTextField4.isEnabled = false
            otpTextField3.isEnabled = true
            otpTextField3.becomeFirstResponder()
        }
    }

    // 11. as the user types it will check which textField you are in and then once the text.count is what you want for that textField it will jump to the next textField
    @objc func monitorTextFieldWhileTyping(_ textField: UITextField) {

        if let text = textField.text {

            if text.count >= 1 {

                // otpTextField1
                if textField == otpTextField1 {

                    if let textInOtpTextField1 = otpTextField1.text {

                        if textInOtpTextField1.count == 3 {
                            otpTextField1.isEnabled = false
                            otpTextField2.isEnabled = true
                            otpTextField2.becomeFirstResponder()
                        }

                        // when the user presses the back button in textInOtpTextField2, now that they're back in textInOtpTextField1 if the conditional statement below is met this will execute
                        if textInOtpTextField1.count > 3 {

                            let firstThreeCharsInTextField1 = textInOtpTextField1.prefix(3)

                            let convertFirstThreeToString = String(firstThreeCharsInTextField1)
                            DispatchQueue.main.async { [weak self] in
                                self?.otpTextField1.text = convertFirstThreeToString
                            }

                            otpTextField1.isEnabled = false
                            otpTextField2.isEnabled = true
                            otpTextField2.becomeFirstResponder()

                            let convertLastCharToString = String(textInOtpTextField1.last!)
                            otpTextField2.text = convertLastCharToString
                        }
                    }
                }

                // otpTextField2
                if textField == otpTextField2 {

                    if let textInOtpTextField2 = otpTextField2.text {

                        if textInOtpTextField2.count == 4 {

                            otpTextField2.isEnabled = false
                            otpTextField3.isEnabled = true
                            otpTextField3.becomeFirstResponder()
                        }

                        // when the user presses the back button in textInOtpTextField3, now that they're back in textInOtpTextField2 if the conditional statement below is met this will execute
                        if textInOtpTextField2.count > 4 {

                            let firstFourCharsInTextField2 = textInOtpTextField2.prefix(4)

                            let convertFirstFourToString = String(firstFourCharsInTextField2)
                            DispatchQueue.main.async { [weak self] in
                                self?.otpTextField2.text = convertFirstFourToString
                            }

                            otpTextField2.isEnabled = false
                            otpTextField3.isEnabled = true
                            otpTextField3.becomeFirstResponder()

                            let convertLastCharToString = String(textInOtpTextField2.last!)
                            otpTextField3.text = convertLastCharToString
                        }
                    }
                }

                // otpTextField3
                if textField == otpTextField3 {

                    if let textInOtpTextField3 = otpTextField3.text {

                        if textInOtpTextField3.count == 7 {
                            otpTextField3.isEnabled = false
                            otpTextField4.isEnabled = true
                            otpTextField4.becomeFirstResponder()
                        }

                        // when the user presses the back button in textInOtpTextField4, now that they're back in textInOtpTextField4 if the conditional statement below is met this will execute
                        if textInOtpTextField3.count > 7 {

                            let firstSevenCharsInTextField3 = textInOtpTextField3.prefix(7)

                            let convertFirstSevenToString = String(firstSevenCharsInTextField3)
                            DispatchQueue.main.async { [weak self] in
                                self?.otpTextField3.text = convertFirstSevenToString
                            }

                            otpTextField3.isEnabled = false
                            otpTextField4.isEnabled = true
                            otpTextField4.becomeFirstResponder()

                            let convertLastCharToString = String(textInOtpTextField3.last!)
                            otpTextField4.text = convertLastCharToString
                        }
                    }
                }

                if textField == otpTextField4 {
                    // do nothing
                }

                textField.text = text
            }
        }
    }

    // 12. Use this delegate method to limit the text in otpTextField4 so that it can only take 1 character
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        if textField == otpTextField4 {

            textField.text = string
            return false
        }
        return true
    }
}

extension ViewController {

    // this adds a lightGray line at the bottom of the textField
    func addBottomLayerTo(textField: UITextField) {
        let layer = CALayer()
        layer.backgroundColor = UIColor.lightGray.cgColor
        layer.frame = CGRect(x: 0, y: textField.frame.height - 2, width: textField.frame.width, height: 2)
        textField.layer.addSublayer(layer)
    }

    func configureAnchors() {

        view.addSubview(staticLabel)

        view.addSubview(otpTextField1)
        view.addSubview(otpTextField2)
        view.addSubview(otpTextField3)
        view.addSubview(otpTextField4)

        let width = view.frame.width / 5

        staticLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15).isActive = true
        staticLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
        staticLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true

        // textField 1
        otpTextField1.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
        otpTextField1.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
        otpTextField1.widthAnchor.constraint(equalToConstant: width).isActive = true
        otpTextField1.heightAnchor.constraint(equalToConstant: width).isActive = true

        // textField 2
        otpTextField2.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
        otpTextField2.leadingAnchor.constraint(equalTo: otpTextField1.trailingAnchor, constant: 10).isActive = true
        otpTextField2.widthAnchor.constraint(equalTo: otpTextField1.widthAnchor).isActive = true
        otpTextField2.heightAnchor.constraint(equalToConstant: width).isActive = true

        // textField 3
        otpTextField3.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
        otpTextField3.leadingAnchor.constraint(equalTo: otpTextField2.trailingAnchor, constant: 10).isActive = true
        otpTextField3.widthAnchor.constraint(equalTo: otpTextField1.widthAnchor).isActive = true
        otpTextField3.heightAnchor.constraint(equalToConstant: width).isActive = true

        // textField 4
        otpTextField4.topAnchor.constraint(equalTo: staticLabel.bottomAnchor, constant: 10).isActive = true
        otpTextField4.leadingAnchor.constraint(equalTo: otpTextField3.trailingAnchor, constant: 10).isActive = true
        otpTextField4.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
        otpTextField4.widthAnchor.constraint(equalTo: otpTextField1.widthAnchor).isActive = true
        otpTextField4.heightAnchor.constraint(equalToConstant: width).isActive = true
    }
}