-> 从iOS 12开始,苹果将允许支持读取iPhone设备中的一次性代码。您可以将文本拆分为四个字段,并自动填充和手动输入otp并逐个删除并移动每个文本字段。
1)self.textone最大长度为4,其他文本字段最大长度为1
2)添加UITextFieldDelegate
if #available(iOS 12.0, *) {
txtOne.textContentType = .oneTimeCode
}
self.txtOne.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
self.txtOne.becomeFirstResponder()
@objc func textFieldDidChange(_ textField: UITextField) {
if #available(iOS 12.0, *) {
if textField.textContentType == UITextContentType.oneTimeCode{
//here split the text to your four text fields
if let otpCode = textField.text, otpCode.count > 3{
txtOne.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 0)])
txtTwo.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 1)])
txtThree.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 2)])
txtFour.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 3)])
}
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (string.count == 1){
if textField == txtOne {
txtTwo?.becomeFirstResponder()
}
if textField == txtTwo {
txtThree?.becomeFirstResponder()
}
if textField == txtThree {
txtFour?.becomeFirstResponder()
}
if textField == txtFour {
txtFour?.resignFirstResponder()
textField.text? = string
//APICall Verify OTP
//Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.VerifyOTPAPI), userInfo: nil, repeats: false)
}
textField.text? = string
return false
}else{
if textField == txtOne {
txtOne?.becomeFirstResponder()
}
if textField == txtTwo {
txtOne?.becomeFirstResponder()
}
if textField == txtThree {
txtTwo?.becomeFirstResponder()
}
if textField == txtFour {
txtThree?.becomeFirstResponder()
}
textField.text? = string
return false
}
}
我在6个不同的UITextFields中使用Firebase OneTimeCode遇到了问题,设法让操作系统从短信中自动填充它,还允许用户复制和粘贴它,当然也可以一次插入一个字符,通过实现shouldChangeCharactersIn方法以非常手动但有效的方式来实现:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//This lines allows the user to delete the number in the textfield.
if string.isEmpty{
return true
}
//----------------------------------------------------------------
//This lines prevents the users from entering any type of text.
if Int(string) == nil {
return false
}
//----------------------------------------------------------------
//This lines lets the user copy and paste the One Time Code.
//For this code to work you need to enable subscript in Strings https://gist.github.com/JCTec/6f6bafba57373f7385619380046822a0
if string.count == 6 {
first.text = "\(string[0])"
second.text = "\(string[1])"
third.text = "\(string[2])"
fourth.text = "\(string[3])"
fifth.text = "\(string[4])"
sixth.text = "\(string[5])"
DispatchQueue.main.async {
self.dismissKeyboard()
self.validCode()
}
}
//----------------------------------------------------------------
//This is where the magic happens. The OS will try to insert manually the code number by number, this lines will insert all the numbers one by one in each TextField as it goes In. (The first one will go in normally and the next to follow will be inserted manually)
if string.count == 1 {
if (textField.text?.count ?? 0) == 1 && textField.tag == 0{
if (second.text?.count ?? 0) == 1{
if (third.text?.count ?? 0) == 1{
if (fourth.text?.count ?? 0) == 1{
if (fifth.text?.count ?? 0) == 1{
sixth.text = string
DispatchQueue.main.async {
self.dismissKeyboard()
self.validCode()
}
return false
}else{
fifth.text = string
return false
}
}else{
fourth.text = string
return false
}
}else{
third.text = string
return false
}
}else{
second.text = string
return false
}
}
}
//----------------------------------------------------------------
//This lines of code will ensure you can only insert one number in each UITextField and change the user to next UITextField when function ends.
guard let textFieldText = textField.text,
let rangeOfTextToReplace = Range(range, in: textFieldText) else {
return false
}
let substringToReplace = textFieldText[rangeOfTextToReplace]
let count = textFieldText.count - substringToReplace.count + string.count
if count == 1{
if textField.tag == 0{
DispatchQueue.main.async {
self.second.becomeFirstResponder()
}
}else if textField.tag == 1{
DispatchQueue.main.async {
self.third.becomeFirstResponder()
}
}else if textField.tag == 2{
DispatchQueue.main.async {
self.fourth.becomeFirstResponder()
}
}else if textField.tag == 3{
DispatchQueue.main.async {
self.fifth.becomeFirstResponder()
}
}else if textField.tag == 4{
DispatchQueue.main.async {
self.sixth.becomeFirstResponder()
}
}else {
DispatchQueue.main.async {
self.dismissKeyboard()
self.validCode()
}
}
}
return count <= 1
//----------------------------------------------------------------
}
textField.delegate = self
textField.textContentType = .oneTimeCode
如果您可以获取单个字段的自动OTP,那么您可以将该文本拆分成四个文本字段。我相信。
您可能需要使用textField的更改观察器,如下所示:
textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
func textFieldDidChange(_ textField: UITextField) {
// here check you text field's input Type
if textField.textContentType == UITextContentType.oneTimeCode{
//here split the text to your four text fields
if let otpCode = textField.text, otpCode.count > 3{
textField.text = String(otpCode[otpCode.startIndex])
textField1.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 1)])
textField2.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 2)])
textField3.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 3)])
}
}
}
UITextFieldDelegate
方法。 在viewDidAppear
上,你的第一个文本字段应该成为第一响应者,并且其类型应为oneTimeCode
。func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Fill your textfields here
return true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.length > 1 {
textFieldDidChange(textField, otpCode: string)
return false
}
}
func textFieldDidChange(_ textField: UITextField, otpCode: String) {
if textField.textContentType == UITextContentType.oneTimeCode{
//here split the text to your four text fields
if otpCode.count == 4, Int(otpCode) != nil {
otp_field_1.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 0)])
otp_field_2.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 1)])
otp_field_3.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 2)])
otp_field_4.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 3)])
let textFields = [otp_field_1, otp_field_2, otp_field_3, otp_field_4]
for i in 0..<textFields.count{
textFields[i].layer.borderColor = UIColor.GREEN_COLOR.cgColor
}
} else {
textField.text = ""
}
textField.resignFirstResponder()
}
}
这是我如何使用多个文本字段、粘贴和删除选项完成的
@IBOutlet weak var otpTextField1: UITextField!
@IBOutlet weak var otpTextField2: UITextField!
@IBOutlet weak var otpTextField3: UITextField!
@IBOutlet weak var otpTextField4: UITextField!
@IBOutlet weak var nextButton: UIButton!
属性
private let maxLengthPhoneNumber = 1
private let acceptableNumbers = "0123456789"
private var OTP = "OTP"
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
otpTextField1.delegate = self
otpTextField1.layer.borderWidth = 1
otpTextField1.layer.cornerRadius = 8
otpTextField1.textContentType = .oneTimeCode
otpTextField1.becomeFirstResponder()
otpTextField2.delegate = self
otpTextField2.layer.borderWidth = 1
otpTextField2.layer.cornerRadius = 8
otpTextField2.textContentType = .oneTimeCode
otpTextField3.delegate = self
otpTextField3.layer.borderWidth = 1
otpTextField3.layer.cornerRadius = 8
otpTextField3.textContentType = .oneTimeCode
otpTextField4.delegate = self
otpTextField4.layer.borderWidth = 1
otpTextField4.layer.cornerRadius = 8
otpTextField4.textContentType = .oneTimeCode
nextButton.isUserInteractionEnabled = false
}
粘贴
private func otpPaste(_ textField: UITextField, _ string: String) {
if textField.textContentType == UITextContentType.oneTimeCode {
otpTextField1.becomeFirstResponder()
//split the text to four text fields
otpTextField1.text = String(string[string.index(string.startIndex, offsetBy: 0)])
otpTextField2.text = String(string[string.index(string.startIndex, offsetBy: 1)])
otpTextField3.text = String(string[string.index(string.startIndex, offsetBy: 2)])
otpTextField4.text = String(string[string.index(string.startIndex, offsetBy: 3)])
otpTextField1.resignFirstResponder()
}
}
OTP发送到API
private func otpSentToApi(_ textField: UITextField) {
if otpTextField1.text != "" && otpTextField2.text != "" && otpTextField3.text != "" && otpTextField4.text != "" {
print( "not '' check")
textField.resignFirstResponder()//
//api call
OTP = "\(otpTextField1.text!)\(otpTextField2.text!)\(otpTextField3.text!)\(otpTextField4.text!)"
print("OTP \(OTP)")
//api success
//unlocked
nextButton.isUserInteractionEnabled = true
}
}
在文本框代表中
extension OTPViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let numberOnly = NSCharacterSet(charactersIn: acceptableNumbers).inverted
let strValid = string.rangeOfCharacter(from: numberOnly) == nil
// number validation
if (strValid) {
// tf paste input
if string.count == 4 {
otpPaste(textField, string) //paste otp
otpSentToApi(textField) //check for not empty & send api
return false
}
// tf single input
else if string.count == 1 {
if textField == otpTextField1 {
otpTextField2.becomeFirstResponder()
textField.text? = string
otpSentToApi(textField) //check for not empty & send api
}
if textField == otpTextField2 {
if otpTextField1.text != "" {
otpTextField3.becomeFirstResponder()
textField.text? = string
}
else {
otpTextField1.becomeFirstResponder()
otpTextField1.text = string
textField.becomeFirstResponder()
}
otpSentToApi(textField) //check for not empty & send api
}
if textField == otpTextField3 {
if otpTextField2.text != "" {
otpTextField4.becomeFirstResponder()
textField.text? = string
}
else {
otpTextField1.becomeFirstResponder()
otpTextField1.text = string
otpTextField2.becomeFirstResponder()
}
otpSentToApi(textField) //check for not empty & send api
}
if textField == otpTextField4 {
if otpTextField2.text != "" {
otpTextField4.resignFirstResponder()
textField.text? = string
}
else {
otpTextField1.becomeFirstResponder()
otpTextField1.text = string
otpTextField2.becomeFirstResponder()
}
otpSentToApi(textField) //check for not empty & send api
}
return false
}
else if string.count == 0 {
if textField == otpTextField4 {
otpTextField3?.becomeFirstResponder()
textField.text? = string
}
if textField == otpTextField3 {
if otpTextField2.text != "" && otpTextField4.text != "" {
textField.becomeFirstResponder()
textField.text = string
}
else {
otpTextField2?.becomeFirstResponder()
textField.text? = string
}
}
if textField == otpTextField2 {
if otpTextField1.text != "" && otpTextField3.text != "" {
textField.becomeFirstResponder()
textField.text = string
}
else {
otpTextField1?.becomeFirstResponder()
textField.text? = string
}
}
if textField == otpTextField1 {
otpTextField1?.becomeFirstResponder()
textField.text? = string
}
// locked
nextButton.isUserInteractionEnabled = false
return false
}
else {
return false
}
}
else {
return strValid
}
}
//textfield func for the touch on BG
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}
UITextField
,它将具有自定义的底部绘制边框,然后您就可以开始使用了!不需要拥有4个文本字段。 - inokey