Move to next UITextField after entering exactly one numeric digit - ios

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)
}

Related

how can I add a addition feature into my Xcode project

I have a textfield in my firstViewContoller and thats where the user types in some numbers. Then in my secondViewController i have a label and in that label I want the number entered by the user to be shown. I have done till this but I dont know how I can take the value the user enters in the textfield every time and add it all up and show the result in the label. For example the user enters 5 so now the label is showing 5 then the user types in 20 and now i want the label to add that up and show 25 and keep on adding what the user enters.
// ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var amountSpent: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
amountSpent.delegate = self
amountSpent.keyboardType = .numberPad }
private func amountSpent(_ amountSpent: UITextField,
shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let invalidCharacters = CharacterSet(charactersIn: "0123456789").inverted
return string.rangeOfCharacter(from: invalidCharacters) == nil }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let resultViewController = segue.destination as! ResultViewController
resultViewController.recievedInt = amountSpent.text!
}
#IBAction func buttonPressed(_ sender: Any) {
self.performSegue(withIdentifier: "goToResult", sender: nil) } }
// ResultViewController.swift
import UIKit
class ResultViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var totalAmount: UILabel!
var recievedInt = ""
override func viewDidLoad() {
super.viewDidLoad()
totalAmount.text = recievedInt
}
}
Convert the new and the existing values from string to Int and add them together before setting the label to the new total
class ResultViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var totalAmount: UILabel!
var recievedInt = ""
override func viewDidLoad() {
super.viewDidLoad()
if let newValue = Int(recievedInt), let total = Int(totalAmount.text!) {
let newTotal = newValue + total
totalAmount.text = "\(newTotal)"
}
}
An alternative way to do it could be
override func viewDidLoad() {
super.viewDidLoad()
let newValue = Int(recievedInt) ?? 0
let total = Int(totalAmount.text!) ?? 0
let newTotal = newValue + total
totalAmount.text = "\(newTotal)"
}
Create a string variable to store the total value
var totalValue = "0"
Add the below method to your project. This will add all your entered values and will return the total value as a string.
private func getTotalCount(string: String) -> String {
let strTotal = "\(totalCount) + \(string)"
let arithmeticExpression = NSExpression(format: strTotal)
let additionResult = arithmeticExpression.expressionValue(with: nil, context: nil) as! Int
return String(additionResult)
}
You will click the done/return button after you entered the count in the text field, the delegate method will be triggered. Call the getTotalCount() method inside the delegate method as below:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
totalCount = self.getTotalCount(string: textField.text ?? "0")
textField.resignFirstResponder()
return true
}
Thus adding new counts will be automatically handled inside the UITextField delegate method. You just need to pass the totalValue to the next View Controller like this:
let resultViewController = segue.destination as! ResultViewController
resultViewController.recievedInt = totalCount

I want to be able to edit text fields in the order specified by the tags. A lot of research to no avail. I am somewhat of a novice

Can not get the the fields to edit in the order of the tags. In this scenario I have four fields on a screen in a two columns and two rows. I want to be able to be able to edit down column 1 and the column2. By default it goes across row 1 then to row2.
This is part of much larger project I am working on. This is a critical feature as there are quite a few more fields (more than 20). I've tried using TextDidEndEditing too. That didn't help.
import UIKit
import os.log
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var Par1: UITextField!
#IBOutlet var Par2: UITextField!
#IBOutlet var Par3: UITextField!
#IBOutlet var Par4: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Par1.delegate = self
Par1.tag = 0
Par2.delegate = self
Par2.tag = 1
Par3.delegate = self
Par3.tag = 2
Par4.delegate = self
Par4.tag = 3
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
var nextTag:Int
textField.resignFirstResponder()
if (textField.tag == 3) {
nextTag = 0
} else {
nextTag = textField.tag + 1
}
if let nextResponder = textField.superview?.viewWithTag(nextTag) as? UITextField {
nextResponder.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return true
}
#IBAction func Par1Entered(_ sender: UITextField) {
Par1.text = sender.text
}
#IBAction func Par2Entered(_ sender: UITextField) {
Par2.text = sender.text
}
#IBAction func Par3Entered(_ sender: UITextField) {
Par3.text = sender.text
}
#IBAction func Par4Entered(_ sender: UITextField) {
Par4.text = sender.text
}
}
Make the field tags sequential and the in textFieldShouldReturn do the following:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
nextResponder.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return false
}
If you want to go to the next field without hitting the Next keyboard button there are a few other ways to achieve this.

iOS: How to get the current visible keyboard type?

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!

How could I enable / disable keyboard Return Key manually in Swift?

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")
}
}
}
}

UITextView becomeFirstResponder() adds new line

I'm quite new to iOS/Swift and I have a strange behaviour when setting a UITextView as a first responder (when touching next from the previous UITextField). It automatically inserts a new line in the UITextView. The code:
class MyViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var descriptionTextView: UITextView!
override func viewDidLoad() {
nameTextField.delegate = self
descriptionTextView.delegate = self
}
func textFieldShouldReturn(nameTextField: UITextField) -> Bool {
nameTextField.resignFirstResponder()
descriptionTextView.becomeFirstResponder()
return true
}
func textView(descriptionTextView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
descriptionTextView.resignFirstResponder()
return false
}
return true
}
}
A new line is always added and i press next, even if there's already text or a new line.
On the first next here is what happens (the cursor is automatically on the 2nd line).
If I go back to the text field and press next again, the cursor is on the 3rd line).
Return false in textFieldShouldReturn
func textFieldShouldReturn(nameTextField: UITextField) -> Bool {
nameTextField.resignFirstResponder()
descriptionTextView.becomeFirstResponder()
return false
}
More details here

Resources