I am working on Swift application, In that I have OTP verification screen. So, User can enters 6 digits otp in 6 textfields. I have created collection view and in that cell I have added dynamically 6 cells. I am able to entering text and validating.
But, My requirement is once first digit fills, automatically cursor should move to next one.
My code is below
var insertedValues = [String]()
private var arrayOfCells: [OtpCollectionViewCell] = []
override func viewDidLoad() {
super.viewDidLoad()
let cellSize = CGSize(width:50 , height:50)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical //.horizontal
layout.itemSize = cellSize
layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
layout.minimumLineSpacing = 1.0
layout.minimumInteritemSpacing = 1.0
collectionView.setCollectionViewLayout(layout, animated: true)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6 // this may be dynamic count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "OtpCollectionViewCell", for: indexPath) as! OtpCollectionViewCell
cell.otpTextField.tag = indexPath.row
cell.otpTextField.delegate = self
arrayOfCells = [cell]
return cell
}
func textFieldDidEndEditing(_ textField: UITextField) {
if insertedValues.count > 0 {
insertedValues.removeAll()
}
for i in 0..<arrayOfCells.count {
if let textfieldText = arrayOfCells[i].otpTextField.text, textfieldText != "" {
insertedValues.append(textfieldText)
if insertedValues.count == 6 {
textField.resignFirstResponder()
self.performAPICall()
}
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if range.location == 0 && string == " " { //restrict single and double space tap
return false
}
let currentCharacterCount = textField.text?.count ?? 0
if range.length range.location > currentCharacterCount {
return false
}
let newLength = currentCharacterCount string.count - range.length
return newLength <= 1
}
Here is my screen looks like.
CodePudding user response:
This is the logic of moving cursor b/w textfields of OTP just map to your collectionview otp cells
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (textField.text?.count)! < 1 && string.count > 0 {
if textField == textOne {
textTwo.becomeFirstResponder()
}
if textField == textTwo {
textThree.becomeFirstResponder()
}
if textField == textThree {
textFour.becomeFirstResponder()
}
if textField == textFour {
textFive.becomeFirstResponder()
}
if textField == textFive {
textSix.becomeFirstResponder()
}
if textField == textSix {
textSix.resignFirstResponder()
}
textField.text = string
return false
}
else if (textField.text?.count)! >= 1 && string.count == 0 {
if textField == textTwo {
textOne.becomeFirstResponder()
}
if (textField == textThree) {
textTwo.becomeFirstResponder()
}
if (textField == textFour) {
textThree.becomeFirstResponder()
}
if (textField == textFive) {
textFour.becomeFirstResponder()
}
if (textField == textSix) {
textFive.becomeFirstResponder()
}
if (textField == textOne) {
textOne.resignFirstResponder()
}
textField.text = string
return false
}
else if (textField.text?.count)! >= 1 {
textField.text = string
return false
}
return true
}
CodePudding user response:
I have followed above solution, But, there is much code if there are more otp textfields. So, I have optimised solution.
Because in future, We may increase OTP textfields count like 10, 12. So, that is the purpose, I followed dynamic cells(Textfields) using collection view.
Here is my code
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (textField.text?.count)! < 1 && string.count > 0 { // for moving cursor to next field
for i in 0..<arrayOfCells.count - 1 {
if textField == arrayOfCells[i].otpTextField {
arrayOfCells[i 1].otpTextField.becomeFirstResponder()
}
}
textField.text = string
return false
}
else if (textField.text?.count)! >= 1 && string.count == 0 {
for i in 0..<arrayOfCells.count - 1 {
if textField == arrayOfCells[i 1].otpTextField {
arrayOfCells[i].otpTextField.becomeFirstResponder()
}
}
textField.text = string
return false
}
else if (textField.text?.count)! >= 1 {
textField.text = string
return false
}
}
This code is working as I expected!