如何以编程方式在 swift ios 中自动将光标从一个文本字段移动到另一个文本字段?

How to move cursor from one text field to another automatically in swift ios programmatically?

    func textFieldDidBeginEditing(textField: UITextField) {
    scrlView.setContentOffset(CGPointMake(0, textField.frame.origin.y-70), animated: true)


    if(textField == firstDigit){

        textField.becomeFirstResponder()
        secondDigit.resignFirstResponder()
    }


    else if(textField == secondDigit){

        textField.becomeFirstResponder()
        thirdDigit.resignFirstResponder()
    }

    else if(textField == thirdDigit){
        //textField.becomeFirstResponder()
        fourthDigit.becomeFirstResponder()
    }

我使用四个文本字段进行一次性密码输入,一次只能输入一个数字。输入数字后,我需要将光标自动移动到下一个文本字段。

设置 textField 委托并添加目标:

override func viewDidLoad() {
        super.viewDidLoad()

        first.delegate = self
        second.delegate = self
        third.delegate = self
        fourth.delegate = self


        first.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
        second.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
        third.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
        fourth.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
    }

现在当文本更改时更改 textField

func textFieldDidChange(textField: UITextField){

    let text = textField.text

    if text?.utf16.count >= 1{
        switch textField{
        case first:
            second.becomeFirstResponder()
        case second:
            third.becomeFirstResponder()
        case third:
            fourth.becomeFirstResponder()
        case fourth:
            fourth.resignFirstResponder()
        default:
            break
        }
    }else{

    }
}

最后,当用户开始编辑明文字段时

extension ViewController: UITextFieldDelegate{
    func textFieldDidBeginEditing(textField: UITextField) {
        textField.text = ""
    }
}

**从 UITextfieldDelegate 函数调用并使下一个文本字段成为第一响应者,无需添加目标并记住在 viewDidLoad 中设置所有文本字段的委托 **

            extension ViewController : UITextFieldDelegate {


            func textFieldShouldReturn(textField: UITextField) -> Bool {

              nextTextFieldToFirstResponder(textField)

              return true;
            }




          func nextTextFieldToFirstResponder(textField: UITextField) {

            if textField == emailTextField
            {
              self.firstNameTextField.becomeFirstResponder()
            }
            else if   textField == firstNameTextField {

              self.lastNameTextField.becomeFirstResponder()
            }

            else if  textField == lastNameTextField {

              self.passwordTextField.becomeFirstResponder()
            }

            else if textField == passwordTextField {

              self.confirmPassTextField.becomeFirstResponder()
            }

            else if textField == confirmPassTextField {
              self.confirmPassTextField.resignFirstResponder()

            }

          }

我尝试了很多代码,最后在 Swift 3.0 最新 [2017 年 3 月]

中对我有用

"ViewController" class 应该继承 "UITextFieldDelegate" 以使此代码正常工作。

class ViewController: UIViewController,UITextFieldDelegate 

添加带有正确标签号的文本字段,此标签号用于根据分配给它的增量标签号将控件带到适当的文本字段。

override func viewDidLoad() {

 userNameTextField.delegate = self

        userNameTextField.tag = 0

        userNameTextField.returnKeyType = UIReturnKeyType.next

        passwordTextField.delegate = self

        passwordTextField.tag = 1


        passwordTextField.returnKeyType = UIReturnKeyType.go

}

在上面的代码中,"returnKeyType = UIReturnKeyType.next" 将使小键盘 return 键显示为 "Next" 您还可以选择 "Join/Go" 等其他选项,基于在您的应用程序上更改值。

这个"textFieldShouldReturn"是UITextFieldDelegate控制的一个方法,这里我们有下一个基于标签值递增的字段选择

func textFieldShouldReturn(_ textField: UITextField) -> Bool

    {

        if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {

            nextField.becomeFirstResponder()

        } else {

            textField.resignFirstResponder()

            return true;

        }

        return false

    }

首先我们需要为 UITextField 设置标签;

func textFieldShouldReturnSingle(_ textField: UITextField , newString : String)
{
    let nextTag: Int = textField.tag + 1

    let nextResponder: UIResponder? = textField.superview?.superview?.viewWithTag(nextTag)
    textField.text = newString
    if let nextR = nextResponder
    {
        // Found next responder, so set it.
        nextR.becomeFirstResponder()
    }
    else
    {
        // Not found, so remove keyboard.
        textField.resignFirstResponder()
    }
}

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

    let newString = ((textField.text)! as NSString).replacingCharacters(in: range, with: string)

    let newLength = newString.characters.count

    if newLength == 1 {
        textFieldShouldReturnSingle(textField , newString : newString)
        return false
    }

    return true
}

注意:UITextField在数字格式中只需要一个字符,即OTP格式。

对于 swift 3

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // On inputing value to textfield
    if ((textField.text?.characters.count)! < 1  && string.characters.count > 0){
        let nextTag = textField.tag + 1;

        // get next responder
        let nextResponder = textField.superview?.viewWithTag(nextTag);
        textField.text = string;

        if (nextResponder == nil){
            textField.resignFirstResponder()
        }
        nextResponder?.becomeFirstResponder();
        return false;
    }
    else if ((textField.text?.characters.count)! >= 1  && string.characters.count == 0){
        // on deleting value from Textfield
        let previousTag = textField.tag - 1;

        // get next responder
        var previousResponder = textField.superview?.viewWithTag(previousTag);

        if (previousResponder == nil){
            previousResponder = textField.superview?.viewWithTag(1);
        }
        textField.text = "";
        previousResponder?.becomeFirstResponder();
        return false;
    }
    return true;
}

更新 Swift 5

的解决方案

在此解决方案中,您将转到下一个字段。当您按下擦除时,将出现在上一个文本字段中。

步骤 1: 设置文本字段选择器

override func viewDidLoad() {
        super.viewDidLoad()

         otpTextField1.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
    otpTextField2.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
    otpTextField3.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
    otpTextField4.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
        
    }

第 2 步:现在我们将处理移动下一个文本字段和擦除文本字段。

@objc func textFieldDidChange(textField: UITextField){
        let text = textField.text
        if  text?.count == 1 {
            switch textField{
            case otpTextField1:
                otpTextField2.becomeFirstResponder()
            case otpTextField2:
                otpTextField3.becomeFirstResponder()
            case otpTextField3:
                otpTextField4.becomeFirstResponder()
            case otpTextField4:
                otpTextField4.resignFirstResponder()
            default:
                break
            }
        }
        if  text?.count == 0 {
            switch textField{
            case otpTextField1:
                otpTextField1.becomeFirstResponder()
            case otpTextField2:
                otpTextField1.becomeFirstResponder()
            case otpTextField3:
                otpTextField2.becomeFirstResponder()
            case otpTextField4:
                otpTextField3.becomeFirstResponder()
            default:
                break
            }
        }
        else{

        }
    }

重要提示:不要忘记设置委托。

使用 textFieldShouldBeginEditing 方法

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
   scrlView.setContentOffset(CGPointMake(0, textField.frame.origin.y-70), 
 animated:true)

   if(textField == firstDigit){

      textField.becomeFirstResponder()
     secondDigit.resignFirstResponder()
   }
   else if(textField == secondDigit){

    textField.becomeFirstResponder()
    thirdDigit.resignFirstResponder()
    }
    else if(textField == thirdDigit){
    //textField.becomeFirstResponder()
    fourthDigit.becomeFirstResponder()
    }
   return true;
}

Swift 3 个代码,用于在 OTP(一次性密码)字段中自动将光标从一个字段移动到另一个字段。

    //Add all outlet in your code. 
    @IBOutlet weak var otpbox1: UITextField!
    @IBOutlet weak var otpbox2: UITextField!
    @IBOutlet weak var otpbox3: UITextField!
    @IBOutlet weak var otpbox4: UITextField!
    @IBOutlet weak var otpbox5: UITextField!
    @IBOutlet weak var otpbox6: UITextField!
    // Add the delegate in viewDidLoad
    func viewDidLoad() {
        super.viewDidLoad()
        otpbox1?.delegate = self
        otpbox2?.delegate = self
        otpbox3?.delegate = self
        otpbox4?.delegate = self
        otpbox5?.delegate = self
        otpbox6?.delegate = self
    }
    func textField(_ textField: UITextField, shouldChangeCharactersIn range:NSRange, replacementString string: String) -> Bool {
        // Range.length == 1 means,clicking backspace
    if (range.length == 0){
        if textField == otpbox1 {
            otpbox2?.becomeFirstResponder()
        }
        if textField == otpbox2 {
            otpbox3?.becomeFirstResponder()
        }
        if textField == otpbox3 {
            otpbox4?.becomeFirstResponder()
        }
        if textField == otpbox4 {
            otpbox5?.becomeFirstResponder()
        }
        if textField == otpbox5 {
            otpbox6?.becomeFirstResponder()
        }
        if textField == otpbox6 {
            otpbox6?.resignFirstResponder() /*After the otpbox6 is filled we capture the All the OTP textField and do the server call. If you want to capture the otpbox6 use string.*/
            let otp = "\((otpbox1?.text)!)\((otpbox2?.text)!)\((otpbox3?.text)!)\((otpbox4?.text)!)\((otpbox5?.text)!)\(string)"
        }
        textField.text? = string
        return false
    }else if (range.length == 1) {
            if textField == otpbox6 {
                otpbox5?.becomeFirstResponder()
            }
            if textField == otpbox5 {
                otpbox4?.becomeFirstResponder()
            }
            if textField == otpbox4 {
                otpbox3?.becomeFirstResponder()
            }
            if textField == otpbox3 {
                otpbox2?.becomeFirstResponder()
            }
            if textField == otpbox2 {
                otpbox1?.becomeFirstResponder()
            }
            if textField == otpbox1 {
                otpbox1?.resignFirstResponder()
            }
            textField.text? = ""
            return false
    }
    return true
    }

Objective c and Swift 4.2 在OTP(一次性密码)字段中自动将光标从一个字段移动到另一个字段

这里我使用一个view controller ]1

然后给出每个TextFiled.Those的Tag值相关参考图片如下所示

为第一个文本文件输入标签值 --> 1,2ndTextfiled ---->2,3rd TextFiled --->3 4rth TextFiled---->4

然后分配 Textfiled Delegates 并编写下面的代码,看看魔术

 - (BOOL)textField:(UITextField *)textField 
shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString 
 *)string
 {
 if ((textField.text.length < 1) && (string.length > 0))
 {
    NSInteger nextTag = textField.tag + 1;
    UIResponder* nextResponder = [textField.superview 
  viewWithTag:nextTag];
    if (! nextResponder){
        [textField resignFirstResponder];
    }
    textField.text = string;
    if (nextResponder)
        [nextResponder becomeFirstResponder];
    return NO;

 }else if ((textField.text.length >= 1) && (string.length == 0)){
    // on deleteing value from Textfield

    NSInteger prevTag = textField.tag - 1;
    // Try to find prev responder
    UIResponder* prevResponder = [textField.superview 
 viewWithTag:prevTag];
    if (! prevResponder){
        [textField resignFirstResponder];
    }
    textField.text = string;
    if (prevResponder)
        // Found next responder, so set it.
        [prevResponder becomeFirstResponder];

    return NO;
}
return YES;

}

swift4.2版本代码

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



    if textField.text!.count < 1 && string.count > 0 {
        let tag = textField.tag + 1;
        let nextResponder = textField.superview?.viewWithTag(tag)

        if   (nextResponder != nil){
            textField.resignFirstResponder()

        }
        textField.text = string;
        if (nextResponder != nil){
            nextResponder?.becomeFirstResponder()

        }
        return false;


    }else if (textField.text?.count)! >= 1 && string.count == 0 {
        let prevTag = textField.tag - 1
        let prevResponser = textField.superview?.viewWithTag(prevTag)
          if (prevResponser != nil){
            textField.resignFirstResponder()
        }
        textField.text = string
        if (prevResponser != nil){
            prevResponser?.becomeFirstResponder()

        }
         return false
    }


    return true;

}

这类似于 UberEats 的 otp 字段。您可以将其复制并粘贴到一个文件中,然后 运行 看看它是如何工作的。但是不要忘记添加 MyTextField class 否则它将不起作用。

如果您希望它在输入数字后自动移动到下一个文本字段,并且在文本字段为空时按下后退按钮仍然能够向后移动,这将对您有所帮助。

就像我说的第一件事,这与 UberEats 的短信文本字段工作方式相似。您不能随便按一个 textField 并 select 它。使用它你只能向前和向后移动。 ux 是主观的,但如果优步使用它,ux 必须是有效的。我说它很相似,因为它们也有一个灰色框覆盖 textField,所以我不确定它背后发生了什么。这是我能得到的最接近的。

首先,您必须使用 subclass UITextField using this answer 来检测何时按下退格键。当按下后退按钮时,您将清除该字段和前一个字段内的所有内容,然后跳转到前一个字段。

其次,一旦字符位于文本字段 using this answer 内,您将不得不阻止用户 select 光标的左侧。您从第一步覆盖了同一个子类中的方法。

第三,您需要检测哪个文本字段当前处于活动状态

第四,您将不得不 运行 在 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool using this YouTube tutorial 中进行一些检查。我在他的作品中添加了一些东西。

我正在以编程方式执行所有操作,因此您可以将整个代码复制并粘贴到一个项目中,然后 运行 它

首先创建一个UITextField的子类,并命名为MyTextField:

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
    }
}

其次在 class 中使用 OTP 文本字段,将 class 设置为使用 UITextFieldDelegate 和 MyTextFieldDelegate,然后创建一个 class 属性 并将其命名为 activeTextField。当 textFieldDidBeginEditing 中的任何文本字段变为活动状态时,您将 activeTextField 设置为该文本字段。在 viewDidLoad 中,将所有文本字段设置为使用两个委托。

确保第一个 otpTextField 已启用,第二个、第三个和第四个 otpTextField 最初全部禁用

import UIKit

// 2. set the class to 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: 25)
        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: 25)
        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: 25)
        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: 25)
        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. when a textField is active set the activeTextField property to that textField
func textFieldDidBeginEditing(_ textField: UITextField) {

    activeTextField = textField
}

// 9. 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()
        otpTextField1.text = ""
    }

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

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

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

    if let text = textField.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 == otpTextField1 {
                otpTextField1.isEnabled = false
                otpTextField2.isEnabled = true
                otpTextField2.becomeFirstResponder()
            }

            if textField == otpTextField2 {
                otpTextField2.isEnabled = false
                otpTextField3.isEnabled = true
                otpTextField3.becomeFirstResponder()
            }

            if textField == otpTextField3 {
                otpTextField3.isEnabled = false
                otpTextField4.isEnabled = true
                otpTextField4.becomeFirstResponder()
            }

            if textField == otpTextField4 {
                // do nothing or better yet do something now that you have all four digits for the sms code. Once the user lands on this textField then the sms code is complete
            }

            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 == otpTextField2 {
                otpTextField2.isEnabled = false
                otpTextField1.isEnabled = true
                otpTextField1.becomeFirstResponder()
                otpTextField1.text = ""
            }

            if textField == otpTextField3 {
                otpTextField3.isEnabled = false
                otpTextField2.isEnabled = true
                otpTextField2.becomeFirstResponder()
                otpTextField2.text = ""
            }

            if textField == otpTextField4 {
                otpTextField4.isEnabled = false
                otpTextField3.isEnabled = true
                otpTextField3.becomeFirstResponder()
                otpTextField3.text = ""
            }

            if textField == otpTextField1 {
                // 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 == otpTextField1 {
                otpTextField1.isEnabled = false
                otpTextField2.isEnabled = true
                otpTextField2.becomeFirstResponder()
            }

            if textField == otpTextField2 {
                otpTextField2.isEnabled = false
                otpTextField3.isEnabled = true
                otpTextField3.becomeFirstResponder()
            }

            if textField == otpTextField3 {
                otpTextField3.isEnabled = false
                otpTextField4.isEnabled = true
                otpTextField4.becomeFirstResponder()
            }

            if textField == otpTextField4 {
                // do nothing or better yet do something now that you have all four digits for the sms code. Once the user lands on this textField then the sms code is complete
            }

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

//**Optional** For a quick setup use this below. Here is how to add a gray line to the textFields and here are the anchors:
    // if your app supports portrait and horizontal your going to have to make some adjustments to this every time the phone rotates
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

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

    // 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
     }
}

这与上面的答案不同,但如果您需要向每个 otpTextField 添加 多个字符,则 .

这里我拿了 4 个 TextField

@IBOutlet var txtOtp: [BottomBorderTextField]!

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

    defer{
        if !string.isEmpty {
            textField.text = string
            textField.resignFirstResponder()
            if let index = self.txtOtp.index(where:{[=10=] === textField}) {
                if index < 3 {
                  self.txtOtp[index + 1].becomeFirstResponder()
                }
            }
        }
    }
    return true
}
//MARK:- IBOutlets    
    @IBOutlet weak var tfFirstDigit: UITextField!
    @IBOutlet weak var tfSecondDigit: UITextField!
    @IBOutlet weak var tfThirdDigit: UITextField!
    @IBOutlet weak var tfFourthDigit: UITextField!

//MARK:- view Life Cycle

    override func viewDidLoad() {
        super.viewDidLoad()

        tfFirstDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
        tfSecondDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
        tfThirdDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
        tfFourthDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)



    }

 //MARK:- Text Field Delegate methods
    @objc func textFieldDidChange(textField: UITextField){
        let text = textField.text

        if (text?.utf16.count)! >= 1{
            switch textField{
            case tfFirstDigit:
                tfSecondDigit.becomeFirstResponder()
            case tfSecondDigit:
                tfThirdDigit.becomeFirstResponder()
            case tfThirdDigit:
                tfFourthDigit.becomeFirstResponder()
            case tfFourthDigit:
                tfFourthDigit.resignFirstResponder()
            default:
                break
            }
        }else{

        }
    }



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

让我们使用 IQKeyboardManager 做一些不同的事情。它工作起来很有魅力。不要忘记为每个文本字段设置委托。

//MARK:- TextField delegate methods
@objc func textFieldDidChange(textField: UITextField){

    if textField.text!.count == 1{
        if IQKeyboardManager.shared().canGoNext{
            IQKeyboardManager.shared().goNext()
        }
    }else{
        if IQKeyboardManager.shared().canGoPrevious{
            IQKeyboardManager.shared().goPrevious()
        }
    }
}

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if string == " "{
        return false
    }else if string.isEmpty{
        return true
    }else if textField.text!.count == 1{
        textField.text = string
        if IQKeyboardManager.shared().canGoNext{
            IQKeyboardManager.shared().goNext()
        }
        return false
    }
    return true
}