这类似于
UberEats 如何设置其OTP字段。您只需将其复制并粘贴到文件中运行以查看其工作原理。但不要忘记添加
MyTextField class,否则它将无法工作。
如果您希望在输入数字后自动移动到下一个文本字段,并仍然能够在 textField 为空时向后移动,那么这将对您有所帮助。
就像我说的第一件事一样,这与 UberEats 如何使用短信文本字段类似。您不能随意按一个textField并选择它。使用此方法,您只能向前和向后移动。UX是主观的,但如果Uber使用它,UX必须有效。我说它类似,因为他们还有一个覆盖textField的灰色框,所以我不确定背后发生了什么。这是我可以得到的最接近的东西。
首先,您需要对 UITextField 进行子类化
使用此答案 来检测删除键是否被按下。当按下返回按钮时,您将删除该字段内的所有内容和上一个字段,然后跳转到上一个字段。
首先,您需要创建一个名为MyTextField
的UITextField子类 (使用此答案),以防止用户在字符输入后选择光标左侧。您需要在第一步中的同一子类中重写该方法。
其次,您需要检测当前活动的textField (使用此答案)。
第三,您需要在func textField(_ textField:UITextField,shouldChangeCharactersIn range:NSRange,replacementString string:String) -> Bool
内运行一些检查 (使用此YouTube教程)。我在他的工作中添加了一些东西。
我正在完全以编程方式进行操作,因此您可以将整个代码复制并粘贴到项目中并运行它。
请注意保留HTML标签。
protocol MyTextFieldDelegate: class {
func textFieldDidDelete()
}
class MyTextField: UITextField {
weak var myDelegate: MyTextFieldDelegate?
override func deleteBackward() {
super.deleteBackward()
myDelegate?.textFieldDidDelete()
}
override func closestPosition(to point: CGPoint) -> UITextPosition? {
let beginning = self.beginningOfDocument
let end = self.position(from: beginning, offset: self.text?.count ?? 0)
return end
}
}
在OTP文本字段类中的第二个,设置该类使用UITextFieldDelegate和MyTextFieldDelegate,然后创建一个类属性并命名为activeTextField
。当任何textField在textFieldDidBeginEditing
中变为活动状态时,将activeTextField
设置为该textField。在viewDidLoad中,将所有textFields都设置为使用这两个代理。
确保第一个otpTextField处于启用状态,而第二个、第三个和第四个otpTextFields则全部处于禁用状态。
import UIKit
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
}()
let otpTextField1: MyTextField = {
let textField = MyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = UIFont.systemFont(ofSize: 25)
textField.autocorrectionType = .no
textField.keyboardType = .numberPad
textField.textAlignment = .center
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
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
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
return textField
}()
var activeTextField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
otpTextField1.delegate = self
otpTextField2.delegate = self
otpTextField3.delegate = self
otpTextField4.delegate = self
otpTextField1.myDelegate = self
otpTextField2.myDelegate = self
otpTextField3.myDelegate = self
otpTextField4.myDelegate = self
configureAnchors()
otpTextField1.becomeFirstResponder()
}
func textFieldDidBeginEditing(_ textField: UITextField) {
activeTextField = textField
}
func textFieldDidDelete() {
if activeTextField == otpTextField1 {
print("backButton was pressed in otpTextField1")
}
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 {
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 {
}
textField.text = string
return false
}
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 {
}
textField.text = ""
return false
}
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 {
}
textField.text = string
return false
}
}
return true
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
addBottomLayerTo(textField: otpTextField1)
addBottomLayerTo(textField: otpTextField2)
addBottomLayerTo(textField: otpTextField3)
addBottomLayerTo(textField: otpTextField4)
}
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
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
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
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
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
}
}
![enter image description here](https://istack.dev59.com/zp0zi.webp)
这与上面的回答无关,但如果您需要向每个otpTextField添加
多个字符,请
参考此答案。