sam sam - 4 months ago 12
iOS Question

Auto Resign and assign first responder in text field in Swift

I have taken 3 text fields and want them to hold just one integer for OTP purpose (It's the need of the project design). When user enters number in first textfield, the responder should automatically get assigned to the second text field and then to the third text field. As soon as user enters last OTP digit in the third textfield, I want to hit the webservice. What I did so far is :

class ActivationCodeVC: UIViewController, UITextFieldDelegate


then

otpField1.delegate = self
otpField2.delegate = self
otpField3.delegate = self


then

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

let text=textField.text
let counter = text?.characters.count
if counter >= 1 {

if textField == otpField1{

otpField2.becomeFirstResponder()

} else if textField == otpField2{

otpField3.becomeFirstResponder()

} else if textField == otpField3{

otpField3.resignFirstResponder()

}

}

return true
}


Cursor should start blinking on the 2nd textfield when user enters digit in the 1st textfield. But it doesn't. After I press the second digit, it enters it to the second textfield and then again cursor doesn't moves to the third, but remains in the second text field. Secondly, when I try to remove digit by backspace, say for, when I try removing 1st textfield digit, it removes digit from the second textfield.

Please help me what methods I should implement and how to achieve the functionality correctly.

I tried this but character is printing in the second text field instead of first and so on.

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

let text = textField.text
// create Range<Index> object from old-style one
let start = text!.startIndex.advancedBy(range.location)
let end = start.advancedBy(range.length)
let indexRange = start..<end
// calculate result string value
let result = text!.stringByReplacingCharactersInRange(indexRange, withString: string)

// let text=textField.text
let tempCount = result.characters.count
if tempCount == 1 {

if textField == otpField1{

otpField2.becomeFirstResponder()

} else if textField == otpField2{

otpField3.becomeFirstResponder()

} else if textField == otpField3{

otpField3.resignFirstResponder()

let tempCode = otpField1.text! + otpField2.text! + otpField3.text!

if !APPDELEGATE.internetStatusFalse {
KVNProgress.showWithStatus("Loading...")
let soapApiObj: SoapApi = SoapApi()
mode = ControllerTypeMode.ACTIVAITONCODE
soapApiObj.resDelegate = self
soapApiObj.responseString = NSMutableString(string: "")
soapApiObj.callActivationCodeApi(tempCode)
}
else{
let obj: SuccessFullPopVC=self.storyboard?.instantiateViewControllerWithIdentifier("successFullPopVC") as! SuccessFullPopVC
obj.titlelbl = LocalizationSystem.sharedLocalSystem().localizedStringForKey("Warning", value: nil)
obj.imgSuccess = UIImage(named: "warning")
obj.messagelbl = LocalizationSystem.sharedLocalSystem().localizedStringForKey("No internet connection.", value: nil)
self.navigationController?.presentViewController(obj, animated: false, completion: nil)
}

}

}

return true
}


Thanks for the final solution:

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

let text = textField.text
// create Range<Index> object from old-style one
let start = text!.startIndex.advancedBy(range.location)
let end = start.advancedBy(range.length)
let indexRange = start..<end
// calculate result string value
let result = text!.stringByReplacingCharactersInRange(indexRange, withString: string)

let newLength = result.characters.count

if textField == otpField1{
if newLength == 1 {
otpField1.text = result
otpField2.becomeFirstResponder()
return false
}

} else if textField == otpField2{
if newLength == 1 {
otpField2.text = result
otpField3.becomeFirstResponder()
return false
}

} else if textField == otpField3{
if newLength == 1 {
otpField3.text = result
return false

}

}

return !(result.characters.count>1)
}


It Works!!

Answer

The shouldChangeCharactersInRange-delegate method get called before the actual text change (character put/delete in your case).

Firstly, calculate string you'll obtain after the input action. Use delegate method arguments (range, string) and textField.text like in a snippet below.

let text = textField.text
// create Range<Index> object from old-style one
let start = text.startIndex.advancedBy(range.location)
let end = start.advancedBy(range.length)
let indexRange = start..<end
// calculate result string value
let result = text.stringByReplacingCharactersInRange(indexRange, withString: string)