我正在构建一个 iPhone 应用程序,用户可以在其中输入 6 位验证码,每个数字都在单独的 UITextField 中。 UI 是使用 UIStackView 以编程方式创建的,以水平排列文本字段。
但是,我面临退格功能的问题。当光标位于第二个占位符并按下退格键时,光标应移动到第一个占位符并删除其内容。不幸的是它不起作用。这适用于其他文本字段,但不适用于第一个占位符。
代码: 导入 UIKit
class SignUpPhoneVerificationViewController: UIViewController, UITextFieldDelegate {
let codeTextFieldStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.addSubview(codeTextFieldStackView)
for i in 0..<6 {
let textField = BackspaceTextField()
textField.borderStyle = .roundedRect
textField.textAlignment = .center
textField.font = UIFont.systemFont(ofSize: 24)
textField.keyboardType = .numberPad
textField.delegate = self
textField.tag = i
textField.translatesAutoresizingMaskIntoConstraints = false
codeTextFieldStackView.addArrangedSubview(textField)
}
// Set up constraints for codeTextFieldStackView (not shown for brevity)
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Only allow numbers
guard CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string)) || string.isEmpty else {
return false
}
if string.isEmpty {
// Backspace was pressed
if textField.text?.isEmpty ?? true {
if let previousTextField = view.viewWithTag(textField.tag - 1) as? UITextField {
previousTextField.text = ""
previousTextField.becomeFirstResponder()
} else {
// Move cursor to the first text field if it's the second field
if textField.tag == 1, let firstTextField = view.viewWithTag(0) as? UITextField {
firstTextField.text = ""
firstTextField.becomeFirstResponder()
}
}
} else {
textField.text = ""
}
return false
}
if let text = textField.text, (text + string).count > 1 {
return false
}
textField.text = string
let nextTag = textField.tag + 1
if let nextResponder = view.viewWithTag(nextTag) {
nextResponder.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
}
class BackspaceTextField: UITextField {
override func deleteBackward() {
if text?.isEmpty ?? true {
if let previousTextField = superview?.viewWithTag(tag - 1) as? UITextField {
previousTextField.text = ""
previousTextField.becomeFirstResponder()
} else {
// Move cursor to the first text field if it's the second field
if tag == 1, let firstTextField = superview?.viewWithTag(0) as? UITextField {
firstTextField.text = ""
firstTextField.becomeFirstResponder()
} else {
super.deleteBackward()
}
}
} else {
text = ""
}
}
}
期望: 当光标位于第二个占位符并按下退格键时,光标应移动到第一个占位符并清除其内容。
实际行动: 光标不会移动到第一个占位符并清除其内容。第一个占位符保留其先前的值。
使用
.tag
属性通常是一个坏主意......它可以很有用,但它也可能导致意想不到的问题。
默认情况下,继承自
UIView
的 UI 元素(几乎是每个元素,包括 UITextField
)的默认 .tag
为 0
来自苹果的文档:
讨论
此方法在当前视图及其所有子视图中搜索指定视图。
因此,
superview?.viewWithTag(0)
返回超级视图。
如果您想继续使用
.tag
来跟踪文本字段,请更改此行:
textField.tag = i
至:
textField.tag = i + 1
现在您的文本字段标签将为 1 到 6,而不是 0 到 5。
另一种(也许更好)的方法是:
BackspaceTextField
数组.firstIndex(of: self)
index - 1
大致如下:
class BackspaceTextField: UITextField {
override func deleteBackward() {
// in case we moved the caret in front of the entered digit
if !(text ?? "").isEmpty {
text = ""
return
}
// make sure we have a superview
guard let sv = superview else {
text = ""
return
}
// get array of superview's subviews that are BackspaceTextField
let rtfArray = sv.subviews.compactMap{$0 as? BackspaceTextField}
guard !rtfArray.isEmpty else {
text = ""
return
}
// get index of self, and make sure it's not the first text field
guard let idx = rtfArray.firstIndex(of: self),
idx > 0
else {
text = ""
return
}
// clear the text of the previous field and set it as first responder
rtfArray[idx - 1].text = ""
rtfArray[idx - 1].becomeFirstResponder()
}
}