Why my label left 1 character when updating UILabel from UITextField? - ios

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.

Related

Move to next UITextField after entering exactly one numeric digit

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

Updating UILabel when one of 2 textfields are updated

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.

Detecting if a "space" has been typed in a UITextField, and moving to a next UITextField

I'm new to Swift and I'm implementing a small form into a solution. I have three UITextFields and I want to make it so that if I type in a word and then type the "spacebar", the cursor will move to the next UITextField (i.e. it will prevent you from typing more than one word into the field).
If I was to do this how would I go about it?
I thought I should overwrite the
textField(_:shouldChangeCharactersInRange:replacementString:)
method - is this correct?
Thank you!
Try this
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// Get your textFields text
let str = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
// Check if the last character is a space
// If true then move to the next textfield
if str.characters.last! == " "{
print("SPACE!")
textField2.becomeFirstResponder()
}
else{
print(str.characters.last!)
}
return true
}
Try this,
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
#IBOutlet weak var textField3: UITextField!
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let str = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
if str.characters.last! == " “{
if textField == textField1{
textField2.becomeFirstResponder()
} else if textField == textField2{
textField3.becomeFirstResponder()
} else if textField == textField3{
// do what you want
}
}
return true
}

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

How to customize numeric input for a UITextField?

I have a UITextField (that represents a tip value) in my Storyboard that starts out as $0.00. If the user types an 8, I want the textField to read $0.08. If the user then types a 3, I want the textField to read $0.83. If the user then types 5, I want the textField to read $8.35. How would I go about changing the input to a UITextField in this manner?
You can do this with the following four steps:
Make your viewController a UITextFieldDelegate by adding that to the class definition.
Add an IBOutlet to your textField by Control-dragging from the UITextField in your Storyboard to your code. Call it myTextField.
In viewDidLoad(), set your viewController as the textField’s delegate.
Implement textField:shouldChangeCharactersInRange:replacementString:.
Take the incoming character and add it to the tip, and then use the String(format:) constructor to format your string.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var myTextField: UITextField!
// Tip value in cents
var tip: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
myTextField.text = "$0.00"
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if let digit = Int(string) {
tip = tip * 10 + digit
textField.text = String(format:"$%d.%02d", tip/100, tip%100)
}
return false
}
}

Resources