I am working on an app in which I have to pass 6 digits OTP through 6 textFields in which you to provide only one character and after that it automatically goes to another textField. I created 6 textFields outlets and used this code. The problem I have is that I want to change the specific textField OTP number but the problem is that if I want to change the OTP number of textfield 4 after tapping cross button it automatically goes to the textfield 3. How can I fix this issue?
The code I used:
import UIKit
class OneTimePasswordViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtOTP1: UITextField!
#IBOutlet weak var txtOTP2: UITextField!
#IBOutlet weak var txtOTP3: UITextField!
#IBOutlet weak var txtOTP4: UITextField!
#IBOutlet weak var txtOTP5: UITextField!
#IBOutlet weak var txtOTP6: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
txtOTP1.delegate = self
txtOTP2.delegate = self
txtOTP3.delegate = self
txtOTP4.delegate = self
txtOTP5.delegate = self
txtOTP6.delegate = self
self.txtOTP1.becomeFirstResponder()
}
}
And here is the code for creating logic:
func textField(_ textField: UITextField, shouldChangeCharactersIn range:NSRange, replacementString string: String) -> Bool {
if (range.length == 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 (range.length == 1) {
if textField == txtOTP6 {txtOTP5?.becomeFirstResponder()}
if textField == txtOTP5 {txtOTP4?.becomeFirstResponder()}
if textField == txtOTP4 {txtOTP3?.becomeFirstResponder()}
if textField == txtOTP3 {txtOTP2?.becomeFirstResponder()}
if textField == txtOTP2 {txtOTP1?.becomeFirstResponder()}
if textField == txtOTP1 {txtOTP1?.resignFirstResponder()}
textField.text? = ""
return false
}
return true
}
You can try My 3rd party library:-
https://github.com/Datt1994/DPOTPView
For Solution:-
In viewDidLoad add tag & delegate to all textfield
import UIKit
class OneTimePasswordViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtOTP1: UITextField!
#IBOutlet weak var txtOTP2: UITextField!
#IBOutlet weak var txtOTP3: UITextField!
#IBOutlet weak var txtOTP4: UITextField!
#IBOutlet weak var txtOTP5: UITextField!
#IBOutlet weak var txtOTP6: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
txtOTP1.delegate = self
txtOTP1.tag = 1000
txtOTP2.delegate = self
txtOTP2.tag = 2000
txtOTP3.delegate = self
txtOTP3.tag = 3000
txtOTP4.delegate = self
txtOTP4.tag = 4000
txtOTP5.delegate = self
txtOTP5.tag = 5000
txtOTP6.delegate = self
txtOTP6.tag = 6000
self.txtOTP1.becomeFirstResponder()
}
}
In UITextFieldDelegate extension implement shouldChangeCharactersIn function like below, It will also work with textField.textContentType = .oneTimeCode
extension OneTimePasswordViewController : UITextFieldDelegate {
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.trimmingCharacters(in: CharacterSet.whitespaces).count != 0 {
textField.text = string
if textField.tag < count*1000 {
let next = textField.superview?.viewWithTag((textField.tag/1000 + 1)*1000)
next?.becomeFirstResponder()
} else if textField.tag == count*1000 {
textField.resignFirstResponder()
}
} else if string.count == 0 { // is backspace
textField.text = ""
}
return false
}
}
Related
I'm completely new to programming. Trying to learn Swift. I've created the UI for my app. A simple data entry app for weight lifting PB's. However when I close the app my data doesn't update to new stored values. How do assign a variable string to each UITextfield entry, which when I close the app it will display its last stored value?
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var benchPressPB: UITextField!
#IBOutlet weak var squatPB: UITextField!
#IBOutlet weak var deadliftPB: UITextField!
#IBOutlet weak var ohpPB: UITextField!
#IBOutlet weak var rackPullPB: UITextField!
#IBOutlet weak var legPressPB: UITextField!
#IBOutlet weak var pullUpsPB: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.benchPressPB.delegate = self
self.squatPB.delegate = self
self.deadliftPB.delegate = self
self.ohpPB.delegate = self
self.rackPullPB.delegate = self
self.legPressPB.delegate = self
self.pullUpsPB.delegate = self
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
P.S this may completely wrong and long already, but currently its achieving what I want it do, just not saving new inputted data. If there's any shorter way to get the keyboard to hide on return, let me know!
Your current code doesn't do anything with the values a user enters into your text fields.
You should
Set up a model object to hold the values that the user enters.
In your textFieldShouldReturn, collect the user input and save it
into your model.
Decide on how you want to persist your app's state so it restores
when the app is launched. At it's simplest, this could be saving each
string to a different key/value pair in UserDefaults, or grouped
together in a dictionary or an array.
The code might look something like this: (not tested. Not even compiled. It will need cleanup before you can use it:
#IBOutlet weak var benchPressPB: UITextField!
#IBOutlet weak var squatPB: UITextField!
#IBOutlet weak var deadliftPB: UITextField!
#IBOutlet weak var ohpPB: UITextField!
#IBOutlet weak var rackPullPB: UITextField!
#IBOutlet weak var legPressPB: UITextField!
#IBOutlet weak var pullUpsPB: UITextField!
var textFields = [UITextField]
var textFieldKeys = [
"benchPressPB",
"squatPB",
"deadliftPB",
"ohpPB",
"rackPullPB",
"legPressPB",
"pullUpsPB"
]
var textFieldStrings = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Note that you can hook up the delegates for your
// text fields in your Storyboard.
self.benchPressPB.delegate = self
self.squatPB.delegate = self
self.deadliftPB.delegate = self
self.ohpPB.delegate = self
self.rackPullPB.delegate = self
self.legPressPB.delegate = self
self.pullUpsPB.delegate = self
textFields = [benchPressPB, squatPB, deadliftPB, ohpPB, rackPullPB, legPressPB, pullUpsPB]
// Read values from UserDefaults into the text fields.
for (index, key) in textFieldKeys.enumerated() {
let aValue = UserDefaults.standard.string(forKey: key)
textFields[index].text = aValue
textFieldStrings.append(aValue ?? "")
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
let newText = textField.text
if let index = textFields.firstIndex(of: textField) {
textFieldStrings[index] = newText
UserDefaults.standard.set(newText, forKey: textFieldKeys[index])
}
return true
}
You can subclass UITextField and add a target for editing changed. Every time your text changes you can simply save its new value into user defaults. To make sure you use a unique key for each field you can override the accessibilityIdentifier and implement didSet to load the old values when you set its identifier:
import UIKit
class PersistentTextField: UITextField, UITextFieldDelegate {
override var accessibilityIdentifier: String? {
didSet {
text = UserDefaults.standard.string(forKey: accessibilityIdentifier ?? "")
}
}
override func didMoveToSuperview() {
addTarget(self, action: #selector(editingChanged), for: .editingChanged)
autocapitalizationType = .none
autocorrectionType = .no
delegate = self
}
#objc func editingChanged(_ textField: UITextField) {
UserDefaults.standard.set(text ?? "", forKey: accessibilityIdentifier ?? "")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
resignFirstResponder()
return true
}
}
Then in your view controller just make sure to set their id when your view loads:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var benchPressPB: PersistentTextField!
#IBOutlet weak var squatPB: PersistentTextField!
#IBOutlet weak var deadliftPB: PersistentTextField!
#IBOutlet weak var ohpPB: PersistentTextField!
#IBOutlet weak var rackPullPB: PersistentTextField!
#IBOutlet weak var legPressPB: PersistentTextField!
#IBOutlet weak var pullUpsPB: PersistentTextField!
override func viewDidLoad() {
super.viewDidLoad()
benchPressPB.accessibilityIdentifier = "bench press"
squatPB.accessibilityIdentifier = "squat"
deadliftPB.accessibilityIdentifier = "dead lift"
ohpPB.accessibilityIdentifier = "ohp"
rackPullPB.accessibilityIdentifier = "rack pull"
legPressPB.accessibilityIdentifier = "leg press"
pullUpsPB.accessibilityIdentifier = "pull ups"
}
}
I want to make a login screen for customer ID which accepts numeric input with a number pad and moves to next UITextField after one number is typed by the user.
I have five UITextField, The first UITextField should become the first responder with a number pad and should progress through the fields without pressing the return key. The four UITextField are,
#IBOutlet weak var customerIdOne: UITextField!
#IBOutlet weak var customerIdTwo: UITextField!
#IBOutlet weak var customerIDThree: UITextField!
#IBOutlet weak var customerIdFive: UITextField!
#IBOutlet weak var customerIdFour: UITextField!
and on pressing the login button, All the values in the UITextField should be concatenated.
#IBAction func loginButton(_ sender: Any) {
custID = "\(customerIdOne.text!)\(customerIdTwo.text!)\(customerIDThree.text!)\(customerIdFour.text!)\(customerIdFive.text!)"
print(custID)
}
I am beginner and i want to know if there are efficient ways to implement this.
currently, I used tags with textFieldShouldReturn Delegate method
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField.tag == 1
{
customerIdOne.resignFirstResponder()
customerIdTwo.becomeFirstResponder()
}
if textField.tag == 2
{
customerIdTwo.resignFirstResponder()
customerIDThree.becomeFirstResponder()
}
if textField.tag == 3
{
customerIDThree.resignFirstResponder()
customerIdFour.becomeFirstResponder()
}
if textField.tag == 4
{
customerIdFour.resignFirstResponder()
customerIdFive.becomeFirstResponder()
}
if textField.tag == 5
{
customerIdFive.resignFirstResponder()
}
return false
}
Step-1
create the IBOutletCollections and set the tag for each textfield for identify which textfield user tapped.
#IBOutlet var customerIdButtons: [UITextField]!
Step-2
create the common extenson for textfield
extension yourViewController : UITextFieldDelegate {
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 == 1 {
textFieldShouldReturnSingle(textField, newString : newString)
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()
// call your method
}
}
}
finally get the all customer ID , then use
#IBAction func loginButton(_ sender: Any) {
var texts: [String] = []
customerIdButtons.forEach { texts.append($0.text!)}
custID = texts.reduce("", +)
print(custID)
}
You can use EditingChanged event to see when one text is entered.
Then you can use viewWithTag method to find the next textfield you want to make FirstResponder. Here is full code you'll need to write for your purpose.
override func viewDidLoad() {
super.viewDidLoad()
self.view.viewWithTag(1)?.becomeFirstResponder()
}
#IBAction func editingChanged(_ sender: UITextField) {
if let nextTextField = self.view.viewWithTag(sender.tag + 1) {
nextTextField.becomeFirstResponder()
}
}
Here is the result:
And after that in button action you can get the texts like below:
#IBOutlet var customerIDTextFields: [UITextField]! //IBOutletCollections of all textfields
#IBAction func loginButtonTapped(_ sender: UIButton) {
var customerIDString = ""
self.customerIDTextFields.forEach { (singleCustomerIDTextField) in
customerIDString.append(singleCustomerIDTextField.text!)
}
print(customerIDString)
}
How do I find out if the keyboard is of type numeric, Twitter, email, etc...?
edit: Is there a way to detect keyboard type without using an outlet?
Consider that you have tow textFields in the ViewController, You will need to implement textFieldShouldBeginEditing method from UITextFieldDelegate protocol, as follows:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tfEmail: UITextField!
#IBOutlet weak var tfPassword: UITextField!
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.keyboardType == .emailAddress {
// this is the tfEmail!
}
if textField.isSecureTextEntry {
// this is tfPassword!
}
}
}
Make sure their delegates are connected to the ViewController, programmatically:
tfEmail.delegate = self
tfPassword.delegate = self
or from the Interface Builder.
Note that you can recognize the keyboard type for the current textField by checking its keyboardType property, which is an instance of UIKeyboardType enum:
The type of keyboard to display for a given text-based view. Used with
the keyboardType property.
What about UITextView?
The same exact functionality should be applied when working with UITextViews, but you need to implement textViewDidBeginEditing(_:) method from UITextViewDelegate protocol instead of implementing textFieldShouldBeginEditing. Again, make sure the delegate of the textView is connected to the ViewController.
Also,
If your main purpose of checking the keyboard type is just for recognizing what is the current responded textField/textView, I suggest to do a direct check:
class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
#IBOutlet weak var tfEmail: UITextField!
#IBOutlet weak var tfPassword: UITextField!
#IBOutlet weak var textViewDescription: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
tfEmail.delegate = self
tfPassword.delegate = self
textViewDescription.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField === tfEmail {
// this is the tfEmail!
}
if textField === tfPassword {
// this is tfPassword!
}
}
func textViewDidBeginEditing(_ textView: UITextView) {
if textView === textViewDescription {
// this is description textview
}
}
}
For more information about === operator you might want to check this question/answers.
Hope this helped.
In addition to Ahmad F 's great answer, this is my approach of getting the current keyboard type, at any time:
Step 1: Delegate UITextField
class File: UIViewController, UITextFieldDelegate{//...}
Update viewDidLoad() to this:
#IBOutlet weak var normalTextField: UITextField!
#IBOutlet weak var numberTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
numberTextField.keyboardType = .numberPad
normalTextField.keyboardType = .default
emailTextField.keyboardType = .emailAddress
numberTextField.delegate = self
normalTextField.delegate = self
emailTextField.delegate = self
}
Step 2: Working with UITextField's methods:
Add a variable called keyboardType, as below:
var keyboardType: UIKeyboardType? = nil
Then, change it whenever a new textField begins editing:
func textFieldDidBeginEditing(_ textField: UITextField) {
keyboardType = textField.keyboardType
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
keyboardType = nil
return true
}
Step 3: Create and call a function like below:
func getCurrentKeyboard() -> String{
if keyboardType == nil{
return "no current keyboard"
}
else if keyboardType == .numberPad{
return "number"
}
else if keyboardType == .emailAddress{
return "email"
}
else{
return "default"
}
}
#IBAction func displayCurrentKeyboard(_ sender: UIButton) {
print(self.getCurrentKeyboard())
}
And this outputs: email / number / no current keyboard / default, depending on the case.
If you want to check which type of keyboard it is with if-else statements, you can change your displayCurrentKeyboard() method to this:
#IBAction func displayCurrentKeyboard(_ sender: UIButton) {
let keyboardString = self.getCurrentKeyboard()
if keyboardString == "number"{
//...
}
else if keyboardString == "email"{
//...
}
else{
//...
}
}
And that's it! You can call this wherever you want in your code with this usage:
let keyboardString = self.getCurrentKeyboard()
NOTE: This method also handles the case of no keyboard visible on the screen, returning no current keyboard, in this case.
Let me know if this helps!
This question is not duplicated from these:
How to disable/enable the return key in a UITextField?
How to enable or disable the keyboard return key
Enable and Disable Keyboard return key on demand in iOS
I have two TextFields.
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
textField1 has the Next button like the Return Key;
textField2 has the Go button like the Return Key;
textField1
textField2
I would like to enable the Go button of the second TextField just if both TextFields are not empty.
I tried to use someTextField.enablesReturnKeyAutomatically with TextFieldDelegate, but did not work.
Thank you for help.
Below: textField2 is disabled as long as textField1 is empty. If the latter is non-empty, we enable textField2, but enable the Go button only if textField2 is non-empty (via .enablesReturnKeyAutomatically property),
/* ViewController.swift */
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// text field delegates
textField1.delegate = self
textField2.delegate = self
// set return key styles
textField1.returnKeyType = UIReturnKeyType.Next
textField2.returnKeyType = UIReturnKeyType.Go
// only enable textField2 if textField1 is non-empty
textField2.enabled = false
// only enable 'go' key of textField2 if the field itself is non-empty
textField2.enablesReturnKeyAutomatically = true
}
// UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
if (textField1.text?.isEmpty ?? true) {
textField2.enabled = false
textField.resignFirstResponder()
}
else if textField == textField1 {
textField2.enabled = true
textField2.becomeFirstResponder()
}
else {
textField.resignFirstResponder()
}
return true
}
}
Runs as follows:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textField1.delegate = self
textField2.delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField === textField2 {
setReturnKeyState(for: textField, isEnabled: shoulEnableReturnKey(), delay: 0.1) // A bit hacky it needs delay here
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField === textField2 {
if var text = textField.text, let range = Range(range, in: text) {
text.replaceSubrange(range, with: string)
setReturnKeyState(for: textField, isEnabled: shoulEnableReturnKey())
}
}
return true
}
private func shoulEnableReturnKey() -> Bool {
textField1.hasText && textField2.hasText
}
}
extension UITextFieldDelegate {
func setReturnKeyState(for textField: UITextField, isEnabled: Bool, delay: Double? = nil) {
textField.enablesReturnKeyAutomatically = false
if textField.delegate != nil {
if let delay = delay {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
textField.setValue(isEnabled, forKeyPath: "inputDelegate.returnKeyEnabled")
}
} else {
textField.setValue(isEnabled, forKeyPath: "inputDelegate.returnKeyEnabled")
}
}
}
}
So I have added targets to my IBActions I have created that occur when the value of a text field changes. When these actions occur, the system should check if the two text fields are both integers. I have set two variables set to false, and they are set to true when both of them are an int. In the IBActions, I have if statements that tell a button to be enabled if both of the variables contain integers. When I run the simulator, this button doesn't enable when both of the text fields contain an integer.
I am new to swift, so if possible, please write all of the code out and where it should be in my code. Here is what I have so far:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var calculatorButton: UIButton!
#IBOutlet weak var inspirationLabel: UILabel!
#IBOutlet weak var beginningLabel: UILabel!
#IBOutlet weak var calculatorContainer: UIView!
#IBOutlet weak var answer1Label: UILabel!
#IBOutlet weak var doneButton: UIButton!
#IBOutlet weak var yourWeightTextField: UITextField!
#IBOutlet weak var calorieNumberTextField: UITextField!
#IBOutlet weak var menuExampleButton: UIButton!
#IBOutlet weak var aboutButton: UIButton!
#IBOutlet weak var calculateButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib
yourWeightTextField.delegate = self
calorieNumberTextField.delegate = self
calculateButton.enabled = false
// Calling the textfield valueChanged Methods
yourWeightTextField.addTarget(self, action:"yourWeightValueChanged:", forControlEvents:.ValueChanged);
calorieNumberTextField.addTarget(self, action:"calorieNumberValueChanged:", forControlEvents:.ValueChanged);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func calculatorButtonTapped(sender: AnyObject) {
calculatorContainer.hidden = false
inspirationLabel.hidden = true
beginningLabel.hidden = true
menuExampleButton.hidden = true
aboutButton.hidden = true
}
var yourWeightFilled = false
var calorieNumberFilled = false
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Find out what the text field will be after adding the current edit
let text = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
// If the textfields have the properties of the function
if textField == yourWeightTextField {
yourWeightFilled = text.toInt() != nil
} else if textField == calorieNumberTextField {
calorieNumberFilled = text.toInt() != nil
}
return true
}
func textFieldShouldReturn(textField: UITextField) -> Bool
{
textField.resignFirstResponder();
return true;
}
// The methods to close the keyboard when editing is finished
#IBAction func yourWeightEditingDidEnd(sender: AnyObject) {
yourWeightTextField.resignFirstResponder()
}
#IBAction func calorieNumberEditingDidEnd(sender: AnyObject) {
calorieNumberTextField.resignFirstResponder()
}
#IBAction func yourWeightValueChanged(sender: AnyObject) {
// If both variables are true and the text fields contain integers, enable button
if self.yourWeightFilled && self.calorieNumberFilled {
self.calculateButton.enabled = true
}
}
#IBAction func calorieNumberValueChanged(sender: AnyObject) {
// If both variables are true and the text fields contain integers, enable button
if self.yourWeightFilled && self.calorieNumberFilled {
self.calculateButton.enabled = true
}
}
}
You should look for EditingChaged event, not ValueChanged
EDIT:
What I mean is to change from:
yourWeightTextField.addTarget(self, action:"yourWeightValueChanged:", forControlEvents:.ValueChanged);
calorieNumberTextField.addTarget(self, action:"calorieNumberValueChanged:", forControlEvents:.ValueChanged);
to :
yourWeightTextField.addTarget(self, action:"yourWeightValueChanged:", forControlEvents:.EditingChanged);
calorieNumberTextField.addTarget(self, action:"calorieNumberValueChanged:", forControlEvents:.EditingChanged);
You simply are looking for wrong event.
If you are looking for a text changed event , then Right Click on the text field select Editing Did End from the Sent Events . You can see a circle on the right end click the circle Hold Down Ctrl and Drag it to your ViewController file. Name the Action you want and . I have provided some screen shots for this.
Here i name the Action TextChanged
I am Using Xcode 7 Swift 2 here
Right Click on the Text Box and You can see Something Like this
Finally You can see the TextChanged event Created. when you type something on a text box and click return this event fires.