Issue in `becomeFirstResponder()` in swift - ios

I have six textfields. Now if all my textfield are filled and tap on any textfield the it should always put focus on sixth textfield & show the keyboard. I have tried below code but it does not show keyboard and only put focus when I tap on sixth textfield. please tell me what is the issue with this ?
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.inputAccessoryView = emptyView
if let textOneLength = textFieldOne.text?.length ,let textTwoLength = textFieldTwo.text?.length ,let textThreeLength = textFieldThree.text?.length , let textFourLength = textFieldFour.text?.length,let textFiveLength = textFieldFive.text?.length , let textSixLength = textFieldSix.text?.length {
if (textOneLength > 0) && (textTwoLength > 0) && (textThreeLength > 0) && (textFourLength > 0) && (textFiveLength > 0) && (textSixLength > 0) {
self.textFieldSix.becomeFirstResponder()
} else if (textOneLength <= 0) && (textTwoLength <= 0) && (textThreeLength <= 0) && (textFourLength <= 0) && (textFiveLength <= 0) && (textSixLength <= 0){
self.textFieldOne.becomeFirstResponder()
}
}
}

I think accepted answer is hack. Other thing that we can do is detect touchDown on UITextField, check if last textField should be in focus and do becomeFirstResponder() on it. Next thing, we should disallow focus other textFields if last should be in focus. We can do that in textFieldShouldBeginEditing method.
Here example of ViewController. Just connect 3 textFields and all should work as expected (Swift 4):
class ViewController: UIViewController {
#IBOutlet weak var firstTextField: UITextField!
#IBOutlet weak var secondTextField: UITextField!
#IBOutlet weak var thirdTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
firstTextField.delegate = self
secondTextField.delegate = self
thirdTextField.delegate = self
firstTextField.addTarget(self, action: #selector(textFieldTouch(_:)), for: .touchDown)
secondTextField.addTarget(self, action: #selector(textFieldTouch(_:)), for: .touchDown)
thirdTextField.addTarget(self, action: #selector(textFieldTouch(_:)), for: .touchDown)
}
#IBAction private func textFieldTouch(_ sender: UITextField) {
if shouldFocusOnLastTextField {
thirdTextField.becomeFirstResponder()
}
}
private var shouldFocusOnLastTextField: Bool {
return firstTextField.text?.isEmpty == false && secondTextField.text?.isEmpty == false && thirdTextField.text?.isEmpty == false
}
}
extension ViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
guard shouldFocusOnLastTextField else { return true }
return textField == thirdTextField
}
}
Other, more simple way to achieve that check the textField that is going to be focused:
extension ViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
guard firstTextField.text?.isEmpty == false &&
secondTextField.text?.isEmpty == false &&
thirdTextField.text?.isEmpty == false &&
textField != thirdTextField else { return true }
thirdTextField.becomeFirstResponder()
return false
}
}

The problem with your implementation is that you are trying to assign the first responder when it was already assign to another textField.
The following code should do the trick:
extension TestViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.inputAccessoryView = UIView()
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if let textOneLength = textFieldOne.text?.count ,let textTwoLength = textFieldTwo.text?.count ,let textThreeLength = textFieldThree.text?.count , let textFourLength = textFieldFour.text?.count,let textFiveLength = textFieldFive.text?.count , let textSixLength = textFieldSix.text?.count {
if (textOneLength > 0) && (textTwoLength > 0) && (textThreeLength > 0) && (textFourLength > 0) && (textFiveLength > 0) && (textSixLength > 0) {
///Check if the sixth textField was selected to avoid infinite recursion
if textFieldSix != textField {
self.textFieldSix.becomeFirstResponder()
return false
}
} else if (textOneLength <= 0) && (textTwoLength <= 0) && (textThreeLength <= 0) && (textFourLength <= 0) && (textFiveLength <= 0) && (textSixLength <= 0){
///Check if the first textField was selected to avoid infinite recursion
if textFieldOne != textField {
self.textFieldOne.becomeFirstResponder()
return false
}
}
}
return true
}
}

It should be work. Are you sure that the simulator has the property to show the keywoard enable?
You can change that property with the keys: Command + Shift + k

I know it is strange for me as well but delaying the focus on text field worked for me.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.textFieldOne.becomeFirstResponder()
}
I added above code in textFieldDidBeginEdting & it worked like magic.

In this case, you'll want to reject attempted edits before they happened; when we wish to reject an attempted edit we will also need to set the sixth textfield as the new first responder.
extension MyViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if textFieldOne.text?.isEmpty == false && textFieldTwo.text?.isEmpty == false && textFieldThree.text?.isEmpty == false && textFieldFour.text?.isEmpty == false && textFieldFive.text?.isEmpty == false && textFieldSix.text?.isEmpty == false {
if textField != textFieldSix {
textFieldSix.becomeFirstResponder()
return false
} else {
return true
}
} else {
return true
}
}
}

1 Set UITextfielddelegate in viewcontroller
2 Set self delegate to every textfield
3 add tag value for every textfield
4 add textfield.becomeFirstResponder()
5 check if condition using tag for take specify textfield value

Reference to this answer https://stackoverflow.com/a/27098244/4990506
A responder object only becomes the first responder if the current
responder can resign first-responder status (canResignFirstResponder)
and the new responder can become first responder.
You may call this method to make a responder object such as a view the first responder. However, you should only call it on that view if
it is part of a view hierarchy. If the view’s window property holds a
UIWindow object, it has been installed in a view hierarchy; if it
returns nil, the view is detached from any hierarchy.
First set delegate to all field
self.textFieldOne.delegate = self
self.textFieldTwo.delegate = self
self.textFieldThree.delegate = self
self.textFieldFour.delegate = self
self.textFieldFive.delegate = self
self.textFieldSix.delegate = self
And then delegate
extension ViewController : UITextFieldDelegate{
func textFieldDidBeginEditing(_ textField: UITextField) {
if self.textFieldOne.hasText ,self.textFieldTwo.hasText ,self.textFieldThree.hasText ,self.textFieldFour.hasText ,self.textFieldFive.hasText ,self.textFieldSix.hasText {
textFieldSix.perform(
#selector(becomeFirstResponder),
with: nil,
afterDelay: 0.1
)
}else if !self.textFieldOne.hasText ,!self.textFieldTwo.hasText ,!self.textFieldThree.hasText ,!self.textFieldFour.hasText ,!self.textFieldFive.hasText ,!self.textFieldSix.hasText {
textFieldOne.perform(
#selector(becomeFirstResponder),
with: nil,
afterDelay: 0.1
)
}
}
}

To check any textfield use this code
for view in self.view.subviews {
if let textField = view as? UITextField {
print(textField.text!)
}
}
** you can also identify a textfiled by using this -
if let txtFieldINeed = self.view.viewWithTag(99) as? UITextField {
print(txtFieldINeed.text!)
}
** then use --
txtFieldINeed.becomeFirstResponder()
** Make sure to turn on this options --
* iOS Simulator -> Hardware -> Keyboard
* Uncheck "Connect Hardware Keyboard"

Related

How to create OTP verification screen and detect delete backward on multiple uitextfield is Swift

so i make this otp screen but i have some catch,
i make this otp screen with bunch of uitextfield and i make the logic of it, but i just cant delete on of the num in the textfield that i make
the textfield wont delete when i fill like the first 2 of my num, even i pressess backButton it wont work.....but it will work when i fill the whole num of textfield, in my case is six.
so i have to fill all six of the number and i can delete the number from the textfield, it wont work if only half fill in the textfield.
heres my code :
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if ((textField.text?.count)! < 1) && (string.count > 0) {
if textField == txtOTP1 {
txtOTP2.becomeFirstResponder()
}
if textField == txtOTP2 {
txtOTP3.becomeFirstResponder()
}
if textField == txtOTP3 {
txtOTP4.becomeFirstResponder()
}
if textField == txtOTP4 {
txtOTP5.becomeFirstResponder()
}
if textField == txtOTP5{
txtOTP6.becomeFirstResponder()
}
if textField == txtOTP6{
txtOTP6.resignFirstResponder()
}
textField.text = string
return false
}else if ((textField.text?.count)! >= 1) && (string.count == 0) {
if textField == txtOTP2{
txtOTP1.becomeFirstResponder()
}
if textField == txtOTP3{
txtOTP2.becomeFirstResponder()
}
if textField == txtOTP4{
txtOTP3.becomeFirstResponder()
}
if textField == txtOTP5{
txtOTP4.becomeFirstResponder()
}
if textField == txtOTP6{
txtOTP5.becomeFirstResponder()
}
if textField == txtOTP1{
txtOTP1.resignFirstResponder()
}
textField.text = ""
return false
}
else if (textField.text?.count)! >= 1 {
textField.text = string
return false
}
return true
}
thats the code i use to make the otp uitextField logic......please tell me i know theres something wrong with my logic, thanks.
i watch a tutorial to make this otp screen in this vid
https://www.youtube.com/watch?v=gZnBXh0TRO8
and according to the maker, he said that to fix this issue i just need to "set user interactions for textfield false and make first textfield first responder", i think i just did that but i maybe i did it wrong....
i really need to fix this guys, thanks.
Instead of fixing that code I prefer to create a custom text field that would inform when the deleteBackward key is pressed. So first subclass a UITextField:
import UIKit
class SingleDigitField: UITextField {
// create a boolean property to hold the deleteBackward info
var pressedDelete = false
// customize the text field as you wish
override func willMove(toSuperview newSuperview: UIView?) {
keyboardType = .numberPad
textAlignment = .center
backgroundColor = .blue
isSecureTextEntry = true
isUserInteractionEnabled = false
}
// hide cursor
override func caretRect(for position: UITextPosition) -> CGRect { .zero }
// hide selection
override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] { [] }
// disable copy paste
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { false }
// override deleteBackward method, set the property value to true and send an action for editingChanged
override func deleteBackward() {
pressedDelete = true
sendActions(for: .editingChanged)
}
}
Now in your ViewCOntroller:
import UIKit
class ViewController: UIViewController {
// connect the textfields outlets
#IBOutlet weak var firstDigitField: SingleDigitField!
#IBOutlet weak var secondDigitField: SingleDigitField!
#IBOutlet weak var thirdDigitField: SingleDigitField!
#IBOutlet weak var fourthDigitField: SingleDigitField!
override func viewDidLoad() {
super.viewDidLoad()
// add a target for editing changed for each field
[firstDigitField,secondDigitField,thirdDigitField,fourthDigitField].forEach {
$0?.addTarget(self, action: #selector(editingChanged), for: .editingChanged)
}
// make the firsDigitField the first responder
firstDigitField.isUserInteractionEnabled = true
firstDigitField.becomeFirstResponder()
}
// here you control what happens to each change that occurs to the fields
#objc func editingChanged(_ textField: SingleDigitField) {
// check if the deleteBackwards key was pressed
if textField.pressedDelete {
// reset its state
textField.pressedDelete = false
// if the field has text empty its content
if textField.hasText {
textField.text = ""
} else {
// otherwise switch the field, resign the first responder and activate the previous field and empty its contents
switch textField {
case secondDigitField, thirdDigitField, fourthDigitField:
textField.resignFirstResponder()
textField.isUserInteractionEnabled = false
switch textField {
case secondDigitField:
firstDigitField.isUserInteractionEnabled = true
firstDigitField.becomeFirstResponder()
firstDigitField.text = ""
case thirdDigitField:
secondDigitField.isUserInteractionEnabled = true
secondDigitField.becomeFirstResponder()
secondDigitField.text = ""
case fourthDigitField:
thirdDigitField.isUserInteractionEnabled = true
thirdDigitField.becomeFirstResponder()
thirdDigitField.text = ""
default:
break
}
default: break
}
}
}
// make sure there is only one character and it is a number otherwise delete its contents
guard textField.text?.count == 1, textField.text?.last?.isWholeNumber == true else {
textField.text = ""
return
}
// switch the textField, resign the first responder and make the next field active
switch textField {
case firstDigitField, secondDigitField, thirdDigitField:
textField.resignFirstResponder()
textField.isUserInteractionEnabled = false
switch textField {
case firstDigitField:
secondDigitField.isUserInteractionEnabled = true
secondDigitField.becomeFirstResponder()
case secondDigitField:
thirdDigitField.isUserInteractionEnabled = true
thirdDigitField.becomeFirstResponder()
case thirdDigitField:
fourthDigitField.isUserInteractionEnabled = true
fourthDigitField.becomeFirstResponder()
default: break
}
case fourthDigitField:
fourthDigitField.resignFirstResponder()
default: break
}
}
}
Xcode 12 sample project

textfield backspace action identify when textfield is empty?

I am trying to create otp textfield using five textfield.All working fine if you add top, but issue is occurred when user try to add textfield empty and trying to backspace and it was not call any delegate method of UItextfiled which I already added.
I tried this :-
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let char = string.cStringUsingEncoding(NSUTF8StringEncoding)!
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92) {
println("Backspace was pressed")
}
return true
}
but it's called when textfield is not empty.
For example :-
In below screen shot add 1 and on two different textfield and third one is empty but when I try to backspace it's need to go in second textfield(third is field is empty) this is what I was facing issue from mine side.
Thanks
followed by #Marmik Shah and #Prashant Tukadiya answer here I add my answer , for quick answer I taken the some code from here
step 1 :
create the IBOutletCollection for your all textfields as well as don't forget to set the tag in all textfields in the sequence order, for e.g [1,2,3,4,5,6]
class ViewController: UIViewController{
#IBOutlet var OTPTxtFields: [MyTextField]! // as well as set the tag for textfield in the sequence order
override func viewDidLoad() {
super.viewDidLoad()
//change button color and other options
OTPTxtFields.forEach { $0.textColor = .red; $0.backspaceTextFieldDelegate = self }
OTPTxtFields.first.becomeFirstResponder()
}
step 2 :
in your current page UITextField delegate method
extension ViewController : UITextFieldDelegate, MyTextFieldDelegate {
func textFieldDidEnterBackspace(_ textField: MyTextField) {
guard let index = OTPTxtFields.index(of: textField) else {
return
}
if index > 0 {
OTPTxtFields[index - 1].becomeFirstResponder()
} else {
view.endEditing(true)
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = ((textField.text)! as NSString).replacingCharacters(in: range, with: string)
if newString.count < 2 && !newString.isEmpty {
textFieldShouldReturnSingle(textField, newString : newString)
// return false
}
return newString.count < 2 || string == ""
//return true
}
override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)) || action == #selector(paste(_:)) {
return false
}
return true
}
func textFieldShouldReturnSingle(_ textField: UITextField, newString : String)
{
let nextTag: Int = textField.tag + 1
textField.text = newString
let nextResponder: UIResponder? = textField.superview?.viewWithTag(nextTag)
if let nextR = nextResponder
{
// Found next responder, so set it.
nextR.becomeFirstResponder()
}
else
{
// Not found, so remove keyboard.
textField.resignFirstResponder()
callOTPValidate()
}
}
}
Step 3:
create the textfield class for access the backward function
class MyTextField: UITextField {
weak var myTextFieldDelegate: MyTextFieldDelegate?
override func deleteBackward() {
if text?.isEmpty ?? false {
myTextFieldDelegate?.textFieldDidEnterBackspace(self)
}
super.deleteBackward()
}
}
protocol MyTextFieldDelegate: class {
func textFieldDidEnterBackspace(_ textField: MyTextField)
}
step - 4
finally follow the #Marmik Shah answer for custom class for your UITextField
Step 5
get the values from each textfield use this
func callOTPValidate(){
var texts: [String] = []
OTPTxtFields.forEach { texts.append($0.text!)}
sentOTPOption(currentText: texts.reduce("", +))
}
func sentOTPOption(currentText: String) {
print("AllTextfieldValue == \(currentText)")
}
You can override the function deleteBackward()
Create a new Class that inherits UITextField and trigger and EditingEnd event.
class MyTextField: UITextField {
override func deleteBackward() {
super.deleteBackward()
print("Backspace");
self.endEditing(true);
}
}
Then, in your Storyboard, add a custom class for the UITextField
Next, in your view controller, in the editingEnd action, you can switch the textfield. For this to work, you will need to set a tag value for each of your textfield.
For example, you have two text fields, tfOne and tfTwo.
tfOne.tag = 1; tfTwo.tag = 2
Now, if currently you are editing tfTwo and backspace is clicked, then you set the currently editing text field to tfOne
class ViewController: UIViewController {
#IBOutlet weak var tfOne: MyTextField!
#IBOutlet weak var tfTwo: MyTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func editingEnded(_ sender: UITextField) {
// UITextField editing ended
if(sender.tag == 2) {
self.tfOne.becomeFirstResponder();
}
}
}
You can give tag to your textfield in sequence like 101,102,103,104,105.
when backspace tapped. check the length of string is equal to 0. then goto textfield.tag - 1 until you get first textfield.like if you are on textfield 102 then goto textfield 102-1 = 101.
Same as when enter any character goto next textfield until you reach to last textfield like if you are on textfield 102 then goto textfield 102+1 = 103.
You can use (self.view.viewWithTag(yourTag) as? UITextField)?.becomeFirstResponder()
I don't have system with me so couldn't able to post code

How to move to the next UITextField automatically in Swift

I have 2 textFields with NumberPad keyboard type
#IBOutlet weak var ourTextField: UITextField!
#IBOutlet weak var forThemTextField: UITextField!
and I want to automatically move to the other textfield (from ourTextField to forThemTextField) after entering two numbers inside the ourTextField then after going to the other textfield (forThemTextField) and entering 2 numbers I want the keyboard hide automatically
I have added a condition to only accept two numbers in my textFields by this code :
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let lengthsDictionary = [ourTextField : 2, forThemTextField: 2]
guard let length = lengthsDictionary[textField] else {
return true
}
let currentCharacterCount = textField.text?.count ?? 0
if (range.length + range.location > currentCharacterCount){
return false
}
let newLength = currentCharacterCount + string.count - range.length
return newLength <= length
}
Use shouldChangeCharactersIn of UITextFieldDelegate method & listen with keyboard string
Use tag of textFields, like tag 1 & tag 2
as
class TestingViewController: UIViewController{
#IBOutlet weak var firstTextField: UITextField!
#IBOutlet weak var secondTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
firstTextField.delegate = self
secondTextField.delegate = self
firstTextField.tag = 1
secondTextField.tag = 2
firstTextField.keyboardType = .numberPad
secondTextField.keyboardType = .numberPad
}
}
extension TestingViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return textField.shouldChangeCustomOtp(textField: textField, string: string)
}
}
extension UITextField {
func shouldChangeCustomOtp(textField:UITextField, string: String) ->Bool {
//Check if textField has two chacraters
if ((textField.text?.count)! == 1 && string.count > 0) {
let nextTag = textField.tag + 1;
// get next responder
var nextResponder = textField.superview?.viewWithTag(nextTag);
if (nextResponder == nil) {
nextResponder = textField.superview?.viewWithTag(1);
}
textField.text = textField.text! + string;
//write here your last textfield tag
if textField.tag == 2 {
//Dissmiss keyboard on last entry
textField.resignFirstResponder()
}
else {
///Appear keyboard
nextResponder?.becomeFirstResponder();
}
return false;
} else if ((textField.text?.count)! == 1 && string.count == 0) {// on deleteing value from Textfield
let previousTag = textField.tag - 1;
// get prev responder
var previousResponder = textField.superview?.viewWithTag(previousTag);
if (previousResponder == nil) {
previousResponder = textField.superview?.viewWithTag(1);
}
textField.text = "";
previousResponder?.becomeFirstResponder();
return false
}
return true
}
}
Output:
In viewDidLoad :-
ourTextField?.addTarget(self, action: #selector(CalculatorViewController.textFieldDidChange(_:)), for: UIControlEvents.editingChanged)
forThemTextField?.addTarget(self, action: #selector(CalculatorViewController.textFieldDidChange(_:)), for: UIControlEvents.editingChanged)
//create function
func textFieldDidChange(_ textField: UITextField) {
if textField == ourTextField {
if (textField.text.count)! >= 2 {
forThemTextField?.becomeFirstResponder()
}
}
else if textField == forThemTextField {
if (textField.text?.count)! >= 2 {
forThemTextField.resignFirstResponder()
}
}
}
Add target to the UITextField (in viewDidLoad()), for example:
mTfActuallyFrom?.addTarget(self, action: #selector(CalculatorViewController.textFieldDidChange(_:)), for: UIControlEvents.editingChanged)
In the delegate function, use UITextField.becomeFirstResponder() to move the focus to the next textfield. Change your conditions to your requirement. For example:
func textFieldDidChange(_ textField: UITextField) {
if textField == mTfActuallyFrom {
if (textField.text?.characters.count)! >= 4 {
mTfActuallyTo?.becomeFirstResponder()
}
}
else if textField == mTfActuallyTo {
if (textField.text?.characters.count)! >= 4 {
dismissKeyboard(gesture: nil)
}
}
}

How to jump to next UITextField when editing has finished?

I want to inspect my current UITextField and jump to the next one in case that editing has finished.
So far I have this code implemented, but for some reason is not working.
IMPORTANT: The code that actually work as expected is Robert approach, see it below. I couldn't vote for him since my reputation is low.
NEW ADDED: I am using this to limit to ONE character
//Limit the length of the UITextfields
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.characters.count + string.characters.count - range.length
print("textField...shouldChangeCharactersInRange method called")
switch textField {
case rateTvA:
return newLength <= limitLength
case rateTvB:
return newLength <= limitLength
case rateTvC:
return newLength <= limitLength
// Do not put constraints on any other text field in this view
// that uses this class as its delegate.
default:
return true
}
}
NOTE: I am expecting to receive just ONE single character for each UITextField
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
let charCount = textField.text!.characters.count
print("textFieldShouldEndEditing method called")
print("How many characters has been typed in? \(charCount)")
switch textField {
case rateTvA:
if(charCount > 0) {
rateTvB.becomeFirstResponder()
return true
}
case rateTvB:
if(charCount > 0) {
rateTvC.becomeFirstResponder()
return true
}
case rateTvC:
if(charCount > 0) {
rateTVD.becomeFirstResponder()
return true
}
default:
return false
}
return false
}
What you need depends upon what you want the user's experience to be.
1) You want to automatically jump to the next text field after the user has entered a single character. In this case you should use the Interface Builder to connect the text field's "Editing Changed" event to an IBAction method in your controller. That IBAction method should perform the first responder switching logic.
2) You want the user to take some action to indicate that editing has completed. That action's handler should then perform the first responder switching logic. I think that the return key is a good way to go. In this case you would use the TextField delegate's textFieldShouldReturn method.
Additionally, you should think about what you want to have happen if the user ends the editing of text field A by clicking in one of text fields B or C (or upon some other UI element that your are presenting); in which case your text field delegate's textFieldXXXXEndEditing methods will be called. The question is: should your textFieldXXXXEndEditing method switch the focus away from the element that the user has chosen?
Finally, you said that your code is not working. What is not working? Is the textFieldShouldEndEditing method not being called? If no then you might not be assigning to the text field's delegate property. Or, you might be confused about what causes the textFieldShouldEndEditing method to be called (my suggestions above should give you some clues).
Here is some code. The outlets should be connected using IB. Also, use IB to connect the Editing Changed event of each of the three text fields to the IBAction method.
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textFieldA: UITextField!
#IBOutlet weak var textFieldB: UITextField!
#IBOutlet weak var textFieldC: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textFieldA.delegate = self
textFieldB.delegate = self
textFieldC.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
print("Should begin editing")
return true;
}
func textFieldDidBeginEditing(textField: UITextField) {
print("Did begin editing")
}
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
print("Should end editing")
return true;
}
func textFieldDidEndEditing(textField: UITextField) {
print("Did end editing")
}
func textFieldShouldClear(textField: UITextField) -> Bool {
print("Should clear")
return true;
}
func textFieldShouldChangeCharactersInRange(textField: UITextField, range: NSRange, replacement: String) -> Bool {
print("Should change")
return true;
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
print("User pressed return: switching to next text field")
switchFields(textField)
return true;
}
#IBAction func textChanged(textField: UITextField) {
print("User entered a character; switching to next text field")
switchFields(textField)
}
private func switchFields(textField: UITextField) {
switch textField {
case textFieldA:
textFieldB.becomeFirstResponder()
case textFieldB:
textFieldC.becomeFirstResponder()
case textFieldC:
textFieldA.becomeFirstResponder()
default:
break
}
}
}
You should probably change the responder in the "textFieldDidEndEditing" delegate call. So take out the xxx.becomeFirstResponder() calls in "textFieldShouldEndEditing" and implement roughly:
optional func textFieldDidEndEditing(_ textField: UITextField) {
switch textField {
case rateTvA:
rateTvB.becomeFirstResponder()
case rateTvB:
rateTvC.becomeFirstResponder()
case rateTvC:
rateTVD.becomeFirstResponder()
default:
return
}
return
I haven't compiled this so beware.
If you don't want to depend on a Third-party or any library you can follow below
Use textfield delegate (Swift, Objective-c)
In Objective c you can do this:
-(BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if (_verifyOTP.text.length >= 1 && range.length == 0)
{
[self.verifyOTP2 becomeFirstResponder];
}
if(_verifyOTP2.text.length >= 1 && range.length == 0) {
[_verifyOTP2 resignFirstResponder];
[self.verifyOTP3 becomeFirstResponder];
}
if (_verifyOTP3.text.length >= 1 && range.length == 0)
{
[_verifyOTP3 resignFirstResponder];
[self.verifyOTP4 becomeFirstResponder];
}
if (_verifyOTP4.text.length >= 1 && range.length == 0)
{
[self.verifyOTP4 becomeFirstResponder];
}
return YES;
}
In swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (_verifyOTP.text.count >= 1 && range.length == 0)
{
self.verifyOTP2.becomeFirstResponder()
}
if(_verifyOTP2.text.count >= 1 && range.length == 0) {
_verifyOTP2.resignFirstResponder()
self.verifyOTP3.becomeFirstResponder()
}
if (_verifyOTP3.text.count >= 1 && range.length == 0)
{
_verifyOTP3.resignFirstResponder()
self.verifyOTP4.becomeFirstResponder()
}
if (_verifyOTP4.text.count >= 1 && range.length == 0)
{
self.verifyOTP4 becomeFirstResponder()
}
return true;
}
Else you can use PinView third party Library
Pin view Github link

How to move cursor from one text field to another automatically in swift ios programmatically?

func textFieldDidBeginEditing(textField: UITextField) {
scrlView.setContentOffset(CGPointMake(0, textField.frame.origin.y-70), animated: true)
if(textField == firstDigit){
textField.becomeFirstResponder()
secondDigit.resignFirstResponder()
}
else if(textField == secondDigit){
textField.becomeFirstResponder()
thirdDigit.resignFirstResponder()
}
else if(textField == thirdDigit){
//textField.becomeFirstResponder()
fourthDigit.becomeFirstResponder()
}
I am using four textfields for OTP entry in which only one number can be entered at a time. After entering the number I need to move the cursor automatically to next textfield.
Set textField delegate and add target:
override func viewDidLoad() {
super.viewDidLoad()
first.delegate = self
second.delegate = self
third.delegate = self
fourth.delegate = self
first.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
second.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
third.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
fourth.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
Now when text changes change textField
func textFieldDidChange(textField: UITextField){
let text = textField.text
if text?.utf16.count >= 1{
switch textField{
case first:
second.becomeFirstResponder()
case second:
third.becomeFirstResponder()
case third:
fourth.becomeFirstResponder()
case fourth:
fourth.resignFirstResponder()
default:
break
}
}else{
}
}
And lastly when user start editing clear textField
extension ViewController: UITextFieldDelegate{
func textFieldDidBeginEditing(textField: UITextField) {
textField.text = ""
}
}
update Solution For Swift 5
In This solution, You will go to next Field. And When You Press Erase will come at previous text field.
Step 1: Set Selector for Text Field
override func viewDidLoad() {
super.viewDidLoad()
otpTextField1.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
otpTextField2.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
otpTextField3.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
otpTextField4.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
}
Step 2: Now We will handle move next text Field and Erase text Field.
#objc func textFieldDidChange(textField: UITextField){
let text = textField.text
if text?.count == 1 {
switch textField{
case otpTextField1:
otpTextField2.becomeFirstResponder()
case otpTextField2:
otpTextField3.becomeFirstResponder()
case otpTextField3:
otpTextField4.becomeFirstResponder()
case otpTextField4:
otpTextField4.resignFirstResponder()
default:
break
}
}
if text?.count == 0 {
switch textField{
case otpTextField1:
otpTextField1.becomeFirstResponder()
case otpTextField2:
otpTextField1.becomeFirstResponder()
case otpTextField3:
otpTextField2.becomeFirstResponder()
case otpTextField4:
otpTextField3.becomeFirstResponder()
default:
break
}
}
else{
}
}
Important Note: Don't Forget To set Delegate.
Swift 3 code to move the cursor from one field to another automatically in OTP(One Time Password) fields.
//Add all outlet in your code.
#IBOutlet weak var otpbox1: UITextField!
#IBOutlet weak var otpbox2: UITextField!
#IBOutlet weak var otpbox3: UITextField!
#IBOutlet weak var otpbox4: UITextField!
#IBOutlet weak var otpbox5: UITextField!
#IBOutlet weak var otpbox6: UITextField!
// Add the delegate in viewDidLoad
func viewDidLoad() {
super.viewDidLoad()
otpbox1?.delegate = self
otpbox2?.delegate = self
otpbox3?.delegate = self
otpbox4?.delegate = self
otpbox5?.delegate = self
otpbox6?.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range:NSRange, replacementString string: String) -> Bool {
// Range.length == 1 means,clicking backspace
if (range.length == 0){
if textField == otpbox1 {
otpbox2?.becomeFirstResponder()
}
if textField == otpbox2 {
otpbox3?.becomeFirstResponder()
}
if textField == otpbox3 {
otpbox4?.becomeFirstResponder()
}
if textField == otpbox4 {
otpbox5?.becomeFirstResponder()
}
if textField == otpbox5 {
otpbox6?.becomeFirstResponder()
}
if textField == otpbox6 {
otpbox6?.resignFirstResponder() /*After the otpbox6 is filled we capture the All the OTP textField and do the server call. If you want to capture the otpbox6 use string.*/
let otp = "\((otpbox1?.text)!)\((otpbox2?.text)!)\((otpbox3?.text)!)\((otpbox4?.text)!)\((otpbox5?.text)!)\(string)"
}
textField.text? = string
return false
}else if (range.length == 1) {
if textField == otpbox6 {
otpbox5?.becomeFirstResponder()
}
if textField == otpbox5 {
otpbox4?.becomeFirstResponder()
}
if textField == otpbox4 {
otpbox3?.becomeFirstResponder()
}
if textField == otpbox3 {
otpbox2?.becomeFirstResponder()
}
if textField == otpbox2 {
otpbox1?.becomeFirstResponder()
}
if textField == otpbox1 {
otpbox1?.resignFirstResponder()
}
textField.text? = ""
return false
}
return true
}
This is similar to how UberEats has their otp fields. You can just copy and paste this into a file and run it to see how it works. But don't forget to add the MyTextField class or it won't work.
If you want it to automatically to move to the next textfield after a number is entered and still be able to move backwards if the back button is pressed WHILE the textField is empty this will help you.
Like the very first thing I said, this is similar to how UberEats has their sms textFields working. You can't just randomly press a textField and select it. Using this you can only move forward and backwards. The ux is subjective but if Uber uses it the ux must be valid. I say it's similar because they also have a gray box covering the textField so I'm not sure what's going on behind it. This was the closest I could get.
First your going to have to subclass UITextField using this answer to detect when the backspace button is pressed. When the back button is pressed your going to erase everything inside that field AND the previous field then jump to the previous field.
Second your going to have to prevent the user from being able to select the left side of the cursor once a char is inside the textField using this answer. You override the method in the same subClass from the first step.
Third you need to detect which textField is currently active using this answer
Fourth your going to have to run some checks inside func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool using this YouTube tutorial. I added some things to his work.
I'm doing everything programmatically so you can copy and paste the entire code into a project and run it
First create a subClass of UITextField and name it MyTextField:
protocol MyTextFieldDelegate: class {
func textFieldDidDelete()
}
// 1. subclass UITextField and create protocol for it to know when the backButton is pressed
class MyTextField: UITextField {
weak var myDelegate: MyTextFieldDelegate? // make sure to declare this as weak to prevent a memory leak/retain cycle
override func deleteBackward() {
super.deleteBackward()
myDelegate?.textFieldDidDelete()
}
// when a char is inside the textField this keeps the cursor to the right of it. If the user can get on the left side of the char and press the backspace the current char won't get deleted
override func closestPosition(to point: CGPoint) -> UITextPosition? {
let beginning = self.beginningOfDocument
let end = self.position(from: beginning, offset: self.text?.count ?? 0)
return end
}
}
Second inside the class with the OTP textfields, set the class to use the UITextFieldDelegate and the MyTextFieldDelegate, then create a class property and name it activeTextField. When whichever textField becomes active inside textFieldDidBeginEditing you set the activeTextField to that. In viewDidLoad set all the textFields to use both delegates.
Make sure the First otpTextField is ENABLED and the second, third, and fourth otpTextFields are ALL initially DIASABLED
import UIKit
// 2. set the class to BOTH Delegates
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
}()
// 3. make each textField of type MYTextField
let otpTextField1: MyTextField = {
let textField = MyTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.font = UIFont.systemFont(ofSize: 25)
textField.autocorrectionType = .no
textField.keyboardType = .numberPad
textField.textAlignment = .center
// **important this is initially ENABLED
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 // **important this is initially DISABLED
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 // **important this is initially DISABLED
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 // **important this is initially DISABLED
return textField
}()
// 4. create this property to know which textField is active. Set it in step 8 and use it in step 9
var activeTextField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
// 5. set the regular UItextField delegate to each textField
otpTextField1.delegate = self
otpTextField2.delegate = self
otpTextField3.delegate = self
otpTextField4.delegate = self
// 6. set the subClassed textField delegate to each textField
otpTextField1.myDelegate = self
otpTextField2.myDelegate = self
otpTextField3.myDelegate = self
otpTextField4.myDelegate = self
configureAnchors()
// 7. once the screen appears show the keyboard
otpTextField1.becomeFirstResponder()
}
// 8. when a textField is active set the activeTextField property to that textField
func textFieldDidBeginEditing(_ textField: UITextField) {
activeTextField = textField
}
// 9. when the backButton is pressed, the MyTextField delegate will get called. The activeTextField will let you know which textField the backButton was pressed in. Depending on the textField certain textFields will become enabled and disabled.
func textFieldDidDelete() {
if activeTextField == otpTextField1 {
print("backButton was pressed in otpTextField1")
// do nothing
}
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 {
// 10. when the user enters something in the first textField it will automatically adjust to the next textField and in the process do some disabling and enabling. This will proceed until the last textField
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 {
// do nothing or better yet do something now that you have all four digits for the sms code. Once the user lands on this textField then the sms code is complete
}
textField.text = string
return false
} // 11. if the user gets to the last textField and presses the back button everything above will get reversed
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 {
// do nothing
}
textField.text = ""
return false
} // 12. after pressing the backButton and moving forward again you will have to do what's in step 10 all over again
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 {
// do nothing or better yet do something now that you have all four digits for the sms code. Once the user lands on this textField then the sms code is complete
}
textField.text = string
return false
}
}
return true
}
//**Optional** For a quick setup use this below. Here is how to add a gray line to the textFields and here are the anchors:
// if your app supports portrait and horizontal your going to have to make some adjustments to this every time the phone rotates
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
addBottomLayerTo(textField: otpTextField1)
addBottomLayerTo(textField: otpTextField2)
addBottomLayerTo(textField: otpTextField3)
addBottomLayerTo(textField: otpTextField4)
}
// this adds a lightGray line at the bottom of the textField
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
// textField 1
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
// textField 2
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
// textField 3
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
// textField 4
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
}
}
This is separate from the answer above but if you need to add multiple characters to each otpTextField then follow this answer.
Firstly we'll need to set the tag for the UITextField;
func textFieldShouldReturnSingle(_ textField: UITextField , newString : String)
{
let nextTag: Int = textField.tag + 1
let nextResponder: UIResponder? = textField.superview?.superview?.viewWithTag(nextTag)
textField.text = newString
if let nextR = nextResponder
{
// Found next responder, so set it.
nextR.becomeFirstResponder()
}
else
{
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let newString = ((textField.text)! as NSString).replacingCharacters(in: range, with: string)
let newLength = newString.characters.count
if newLength == 1 {
textFieldShouldReturnSingle(textField , newString : newString)
return false
}
return true
}
Note: The UITextField takes only one character in number format, which is in OTP format.
Objective c and Swift 4.2 to move the cursor from one field to another automatically in OTP(One Time Password) fields
Here i am taking one view controller
]1
Then give the Tag values for each TextFiled.Those related reference images are shown below
Enter tag value for first textfiled --> 1,2ndTextfiled ---->2,3rd TextFiled --->3 4rth TextFiled---->4
Then assign Textfiled Delegates and write below code and see the magic
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString
*)string
{
if ((textField.text.length < 1) && (string.length > 0))
{
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview
viewWithTag:nextTag];
if (! nextResponder){
[textField resignFirstResponder];
}
textField.text = string;
if (nextResponder)
[nextResponder becomeFirstResponder];
return NO;
}else if ((textField.text.length >= 1) && (string.length == 0)){
// on deleteing value from Textfield
NSInteger prevTag = textField.tag - 1;
// Try to find prev responder
UIResponder* prevResponder = [textField.superview
viewWithTag:prevTag];
if (! prevResponder){
[textField resignFirstResponder];
}
textField.text = string;
if (prevResponder)
// Found next responder, so set it.
[prevResponder becomeFirstResponder];
return NO;
}
return YES;
}
swift4.2 version code
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text!.count < 1 && string.count > 0 {
let tag = textField.tag + 1;
let nextResponder = textField.superview?.viewWithTag(tag)
if (nextResponder != nil){
textField.resignFirstResponder()
}
textField.text = string;
if (nextResponder != nil){
nextResponder?.becomeFirstResponder()
}
return false;
}else if (textField.text?.count)! >= 1 && string.count == 0 {
let prevTag = textField.tag - 1
let prevResponser = textField.superview?.viewWithTag(prevTag)
if (prevResponser != nil){
textField.resignFirstResponder()
}
textField.text = string
if (prevResponser != nil){
prevResponser?.becomeFirstResponder()
}
return false
}
return true;
}
Here I was take 4 TextField
#IBOutlet var txtOtp: [BottomBorderTextField]!
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
defer{
if !string.isEmpty {
textField.text = string
textField.resignFirstResponder()
if let index = self.txtOtp.index(where:{$0 === textField}) {
if index < 3 {
self.txtOtp[index + 1].becomeFirstResponder()
}
}
}
}
return true
}
**call from UITextfieldDelegate function and make next text field the first responder and no need to add target and remember to set delegates of all text fields in viewDidLoad **
extension ViewController : UITextFieldDelegate {
func textFieldShouldReturn(textField: UITextField) -> Bool {
nextTextFieldToFirstResponder(textField)
return true;
}
func nextTextFieldToFirstResponder(textField: UITextField) {
if textField == emailTextField
{
self.firstNameTextField.becomeFirstResponder()
}
else if textField == firstNameTextField {
self.lastNameTextField.becomeFirstResponder()
}
else if textField == lastNameTextField {
self.passwordTextField.becomeFirstResponder()
}
else if textField == passwordTextField {
self.confirmPassTextField.becomeFirstResponder()
}
else if textField == confirmPassTextField {
self.confirmPassTextField.resignFirstResponder()
}
}
I have tried many codes and finally this worked for me in Swift 3.0 Latest [March 2017]
The "ViewController" class should inherited the "UITextFieldDelegate" for making this code working.
class ViewController: UIViewController,UITextFieldDelegate
Add the Text field with the Proper Tag nuber and this tag number is used to take the control to appropriate text field based on incremental tag number assigned to it.
override func viewDidLoad() {
userNameTextField.delegate = self
userNameTextField.tag = 0
userNameTextField.returnKeyType = UIReturnKeyType.next
passwordTextField.delegate = self
passwordTextField.tag = 1
passwordTextField.returnKeyType = UIReturnKeyType.go
}
In the above code, the "returnKeyType = UIReturnKeyType.next" where will make the Key pad return key to display as "Next" you also have other options as "Join/Go" etc, based on your application change the values.
This "textFieldShouldReturn" is a method of UITextFieldDelegate controlled and here we have next field selection based on the Tag value incrementation
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
return true;
}
return false
}
Use textFieldShouldBeginEditing method
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
scrlView.setContentOffset(CGPointMake(0, textField.frame.origin.y-70),
animated:true)
if(textField == firstDigit){
textField.becomeFirstResponder()
secondDigit.resignFirstResponder()
}
else if(textField == secondDigit){
textField.becomeFirstResponder()
thirdDigit.resignFirstResponder()
}
else if(textField == thirdDigit){
//textField.becomeFirstResponder()
fourthDigit.becomeFirstResponder()
}
return true;
}
//MARK:- IBOutlets
#IBOutlet weak var tfFirstDigit: UITextField!
#IBOutlet weak var tfSecondDigit: UITextField!
#IBOutlet weak var tfThirdDigit: UITextField!
#IBOutlet weak var tfFourthDigit: UITextField!
//MARK:- view Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
tfFirstDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
tfSecondDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
tfThirdDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
tfFourthDigit.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: UIControl.Event.editingChanged)
}
//MARK:- Text Field Delegate methods
#objc func textFieldDidChange(textField: UITextField){
let text = textField.text
if (text?.utf16.count)! >= 1{
switch textField{
case tfFirstDigit:
tfSecondDigit.becomeFirstResponder()
case tfSecondDigit:
tfThirdDigit.becomeFirstResponder()
case tfThirdDigit:
tfFourthDigit.becomeFirstResponder()
case tfFourthDigit:
tfFourthDigit.resignFirstResponder()
default:
break
}
}else{
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
let do something different using IQKeyboardManager.It work like charm. Do not forget set delegate for every text field.
//MARK:- TextField delegate methods
#objc func textFieldDidChange(textField: UITextField){
if textField.text!.count == 1{
if IQKeyboardManager.shared().canGoNext{
IQKeyboardManager.shared().goNext()
}
}else{
if IQKeyboardManager.shared().canGoPrevious{
IQKeyboardManager.shared().goPrevious()
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string == " "{
return false
}else if string.isEmpty{
return true
}else if textField.text!.count == 1{
textField.text = string
if IQKeyboardManager.shared().canGoNext{
IQKeyboardManager.shared().goNext()
}
return false
}
return true
}
for swift 3
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// On inputing value to textfield
if ((textField.text?.characters.count)! < 1 && string.characters.count > 0){
let nextTag = textField.tag + 1;
// get next responder
let nextResponder = textField.superview?.viewWithTag(nextTag);
textField.text = string;
if (nextResponder == nil){
textField.resignFirstResponder()
}
nextResponder?.becomeFirstResponder();
return false;
}
else if ((textField.text?.characters.count)! >= 1 && string.characters.count == 0){
// on deleting value from Textfield
let previousTag = textField.tag - 1;
// get next responder
var previousResponder = textField.superview?.viewWithTag(previousTag);
if (previousResponder == nil){
previousResponder = textField.superview?.viewWithTag(1);
}
textField.text = "";
previousResponder?.becomeFirstResponder();
return false;
}
return true;
}

Resources