Basically, I am trying to develop an app that has 3 labels, 2 editable & 1 which is not. The 2 editable texts are parameters in a formula, when one of them is updated, the uneditable label should automatically be updated (Think about this as y = a + b, if a is updated, y should automatically be updated).
My problem is that while I formulated a function for the y formula above, I can't seem to get y to update automatically. I have used UITextFieldDelegate's function textField as below:
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var weight: UITextField!
#IBOutlet weak var length: UITextField!
#IBOutlet weak var bmi: UILabel!
#IBOutlet weak var bmiCategory: UILabel!
#IBOutlet weak var textField1: UILabel!
#IBOutlet weak var textField2: UILabel!
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
bmi.text = String(bmiCalculator())
return true
}
func bmiCalculator() -> Float {
let weightValue: Float
let lengthValue: Float
let bmiValue: Float
weightValue = (weight.text! as NSString).floatValue
lengthValue = (length.text! as NSString).floatValue
bmiValue = Float(formatFloat(value: weightValue / (lengthValue * lengthValue)))!
print(bmiValue)
return bmiValue
}
func formatFloat(value: Float) -> String {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 1
let result = formatter.string(from: value as NSNumber)
return result!
}
}
Note: The code will execute and the build will succeed, but the label will not be automatically updated.
You're attempting the calculation too soon. You've been asked whether the field should change and you're calling a method to use existing values before you respond to allow it.
I suggest handling the UITextFieldTextDidChange notification and updating your dependent label there.
Found the answer in a different way: I defined "a" & "b" as both IBOutlets and IBActions, where the IBAction would execute a function that will call the function bmiCalculator() upon an update in the textfield. see below:
#IBAction func weightFieldUpdate(_ textField: UITextField) {
bmi.text = String(bmiCalculator())
}
#IBAction func lengthFieldUpdate(_ textField: UITextField) {
bmi.text = String(bmiCalculator())
}
EDIT: I also found out that this functionality can be done with only 1 IBAction since the same code is executing
#IBAction func weightAndLengthUpdate(_ textField: UITextField) {
bmi.text = String(bmiCalculator())
}
All thats required is to link the two UITextFields to this IBAction.
Related
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
}
}
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)
}
so I am making UILabel live update from UiTextfield (user input). I am using the code from this thread Swift3: Live UiLabel update on user input
but somehow, my UILabel always left one character when I fully erase the text in my UITextField. like the .gif in here http://g.recordit.co/SPQWnYtHJg.gif
and it seems one character is always missing like the picture below
here is the code I use
import UIKit
class CreateEventVC: UIViewController {
#IBOutlet weak var eventNameTextField: UITextField!
#IBOutlet weak var eventNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//initial value
eventNameLabel.text = " "
// delegate declaration
eventNameTextField.delegate = self
}
}
extension CreateEventVC : UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
eventNameLabel.text = eventNameTextField.text
return true
}
}
I initially suspect because I add this line in viewDidload
eventNameLabel.text = " "
but if i delete this one, the problem is still there
what should I do ?
textField:shouldChangeCharactersIn:range:replacementString is called before the change is applied to the text field, this allows your app to veto the request and filter out unwanted content.
The problem is, you're relying on the text field's text. Instead, you need build the resulting value from the information passed to the delegate and apply that
Maybe something more like...
extension CreateEventVC: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = textField.text ?? ""
eventNameLabel.text = (text as NSString).replacingCharacters(in: range, with: string)
return true;
}
}
class CreateEventVC: UIViewController {
#IBOutlet weak var eventNameTextField: UITextField!
#IBOutlet weak var eventNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//initial value
eventNameLabel.text = " "
eventNameTextField.addTarget(self, action: #selector(onTextFieldTextDidChange), for: .editingChanged)
}
#objc func onTextFieldTextDidChange() {
eventNameLabel.text = eventNameTextField.text
}
}
Explanations:
We add target to the eventNameTextField which will call the onTextFieldTextDidChange func each time the textField text is changed.
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!
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.