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
Related
I have a container view with multiple text boxes on it. I also have a button in Parent View controller(custom keypad). What I'm trying to do is select text box first & when I tap on the button I wanted some value to be populated to that last selected/focused textbox.
How can I do that? any alternative ways are welcome too. (I am having multiple container-views in the original code and try to use one keypad for all the views)
class MainViewController: UIViewController {
var weightVC : WeightViewController!
var focusedElement : UITextField
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "weight") {
weightVC = segue.destination as? WeightViewController
}
}
#IBAction func button1Clicked(_ sender: Any) {
if weightVC != nil {
weightVC.sampleTextBox1.text = "1"
//I want this sampleTextBox1 to be dynamic like weightVC.focusedInput = "1"
}
}
}
extension MainViewController:ChildToParentProtocol {
func setFocusedElement(with value: UITextField){
focusedElement = value
}
}
Container View Controller
protocol ChildToParentProtocol: class {
func setFocusedElement(with value:UITextField)
}
class WeightViewController: UIViewController {
weak var delegate: ChildToParentProtocol? = nil
#IBOutlet weak var sampleTextBox1: UITextField!
#IBOutlet weak var sampleTextBox2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
// sampleTextBox1 Editing Did Begin event
#IBAction func editBeginSampleText1(_ sender: Any) {
print("edit begin")
delegate?.setFocusedElement(with: sampleTextBox1)
}
}
In other words, I simply want to keep a reference to last focused UITextFild when a button is tapped. Hope my requirement is clear enough. Please guide me if there is a way to achieve this.
Thanks
If I understood your question correctly you can keep track on which UITextField is tapped by using it's tag. And you can use UITextFieldDelegate to get the selected UITextField tag.
Consider the below code for WeightViewController
protocol ChildToParentProtocol: class {
//Changed value to Int for passing the tag.
func setFocusedElement(with value: Int)
}
import UIKit
class WeightViewController: UIViewController {
#IBOutlet weak var tf1: UITextField!
#IBOutlet weak var tf2: UITextField!
var selectedTFTag = 0
weak var delegate: ChildToParentProtocol? = nil
override func viewDidLoad() {
super.viewDidLoad()
//Assign delegate and tags to your TF
tf1.delegate = self
tf2.delegate = self
tf1.tag = 1
tf2.tag = 2
}
}
extension WeightViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
//Get the selected TF tag
selectedTFTag = textField.tag
//Pass tag to parent view
delegate?.setFocusedElement(with: selectedTFTag)
}
}
Now in your parent view ViewController you need to make some modification. I have added comments where I made changes to achieve your requirement.
import UIKit
//You need to confirm your ChildToParentProtocol with your UIViewController
class ViewController: UIViewController, ChildToParentProtocol {
var selectedTFTag = 0
var weightVC : WeightViewController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "weight" {
weightVC = segue.destination as? WeightViewController
//You need to pass delegate to containerview to make it working.
weightVC.delegate = self
}
}
#IBAction func btn1Tapped(_ sender: Any) {
//According to selected Tag perform your action
if selectedTFTag > 0 {
switch selectedTFTag {
case 1:
//set up first UITextField
weightVC.tf1.text = "First textfield was selected"
print("1")
case 2:
//set up second UITextField
weightVC.tf2.text = "Second textfield was selected"
default:
break
}
}
}
#IBAction func btn2Tapped(_ sender: Any) {
//According to selected Tag perform your action
if selectedTFTag > 0 {
switch selectedTFTag {
case 1:
//set up first UITextField
weightVC.tf1.text = "First textfield was selected"
print("1")
case 2:
//set up second UITextField
weightVC.tf2.text = "Second textfield was selected"
default:
break
}
}
}
func setFocusedElement(with value: Int) {
//Get selected TF tag with delegate
selectedTFTag = value
}
}
You can check THIS demo project for more info.
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.
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)
}
I want to print the total of all of the textfields connected to cool textfield collection. There are only 2, in the log file.
import UIKit
class ViewController: UIViewController {
#IBOutlet var cool: [UITextField]!
#IBAction func press(_ sender: Any) {
for view in cool {
((cool.text! as NSString).integerValue += ((cool.text! as NSString).integerValue
}
}
}
If you want to add up all of the text fields, then simply do:
#IBAction func press(_ sender: Any) {
var total = 0
for view in cool {
if let text = view.text, let num = Int(text) {
total += num
}
}
print("The total is \(total)")
}
Don't force unwrap optionals. Don't use NSString in Swift.
I am trying to use an xib file to display a custom alter view. For that purpose i use the SimpleAlert from here. I have the .xib file with my textFields in it and as the owner of the .xib i set the ViewController is created.
import UIKit
class TestBaumViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tfFirst: UITextField!
#IBOutlet weak var tfSecond: UITextField!
#IBOutlet weak var tfThird: UITextField!
#IBOutlet weak var tfFourth: UITextField!
init() {
super.init(nibName: "TestBaumViewController", bundle: nil)
initializeTextFields()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//initializeTextFields()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
//initializeTextFields()
}
override func awakeFromNib() {
super.awakeFromNib()
//initializeTextFields()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func initializeTextFields() {
tfFirst.delegate = self // Error Here!
tfFirst.keyboardType = UIKeyboardType.ASCIICapable
tfSecond.delegate = self
tfSecond.keyboardType = UIKeyboardType.NumberPad
tfThird.delegate = self
tfThird.keyboardType = UIKeyboardType.ASCIICapable
tfFourth.delegate = self
tfFourth.keyboardType = UIKeyboardType.NumberPad
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
var result = true
let prospectiveText = (textField.text as NSString).stringByReplacingCharactersInRange(range , withString: string)
if textField == tfFirst {
if count(string) > 0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "BW").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLengthIsLegal = count(prospectiveText) <= 1
result = replacementStringIsLegal && resultingStringLengthIsLegal
}
}
if textField == tfSecond {
if count(string) > 0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLenthIsLegel = count(prospectiveText) <= 3
result = replacementStringIsLegal && resultingStringLenthIsLegel
}
}
if textField == tfThird {
if count(string) > 0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyz").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLenthIsLegel = count(prospectiveText) <= 1
result = replacementStringIsLegal && resultingStringLenthIsLegel
}
}
if textField == tfFourth {
if count(string) > 0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLenthIsLegel = count(prospectiveText) <= 3
result = replacementStringIsLegal && resultingStringLenthIsLegel
}
}
return result
}
As you can see i added IBOutlets for my textfields and now i wanted to add this view controller as their delegate. So i can check what was entered in the textfields.
I tried different approaches. First i tried putting the textfield.delegate = self into the viewDidLoad method but there i get an error "fatal error: unexpectedly found nil while unwrapping an Optional value" i think that is because the textfield aren't created jet. Next it tried the viewDidAppear method, with the same result. And finally, after i added an Object to the .xib with the view controller class set as its class to get the awakeFromNib method to fire, i put the textfield.delegate = self into that method. But i get the same error here.
Is there something i did not think of or am i doing something that will not be working?
regards Adarkas
Since you have already declared that your controller is conforming to the UITextFieldDelegate protocol, do this in the Interface Builder:
Select your textfield
Show Connections Inspector
There will be an Outlets section with delegate under it.
Drag the circle on the right of delegate to your View Controller object as shown under
Do this for every textField
The textField delegate methods should be called properly now.
What about didSet?
class TestBaumViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tfFirst: UITextField! {
didSet {
tfFirst.delegate = self
}
}
...
Or you can also drag the text fields delegates in the XIB.
Right click on the text field and drag the delegate property to the view controller.