TextField delegate shouldChangeCharactersInRange - swift

Why doesn't the shouldChangeCharactersInRange update the textfield right away. It is 1 delayed by one character.
For example, download this file: https://www.dropbox.com/s/goljs3d6lcxutxy/UITextField%20Tutorial.zip?dl=0
add the following code to
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
method
print(textField.text)
see how it is delayed by one character. I want it to be updated as I type the text not one delayed.

That method is asking you if it should take the replacementString and add it onto textField.text. It is called immediately after you press a key on the keyboard and before letter appears on screen.
To see what the new string will be, you'd need to to something like this.
let newText = textField.text.stringByReplacingCharactersInRange(range, withString: string)
print(newText)

Related

how to remove character when backspace is pressed swift

i want to remove one last character when user presses the backspace
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty {
print("backspace pressed")
if let itemToRemove = textField.text?.dropLast(){
let text = textField.text?.replacingOccurrences(of: itemToRemove, with: "")
textField.text = text
return true
}
}
return true
}
this function clears all the elements present in the textfield
You're using this method wrong. The delegate method is not for you to implement the change to the text, it's for you to approve the change (hence returning a bool).
From the documentation (which is always a good first point of call if something isn't working how you expect):
Return Value
true if the specified text range should be replaced;
otherwise, false to keep the old text.
Discussion
The text field calls this method whenever user actions cause its text to change. Use this
method to validate text as it is typed by the user. For example, you
could use this method to prevent the user from entering anything but
numerical values.
EDIT: (as pointed out by Duncan C in the comments, and as should have been in the original answer)
A good starting point is just to return true from this method, as then all the user input will be reflected in the text field. If you need to be more specific about what edits you allow you can introduce that logic later.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Backspace handled
guard !string.isEmpty else {
return true
}
return true
}

UITextField shouldChangeCharacters in swift is not working for the first letter entered

I have a textfield population phone number. When i am entering phone number i am checking validation for a invalid phone number. i am using delegate method "textfield should change characters".It is working fine but it is not working for the first letter entered by the user.
extension phoneCell: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
validateInput(of: textField)
return true
}
Can any one help me to resolve this?
If you noticed then shouldChangeCharactersIn has a return type Bool.
What it means is that when a user presses a key on the keyboard, you'll get a callback by this delegate before even registering that character in the textfield.
Now if you return true, that pressed character will be reflected in the textfield if you return false, input will be discarded.
So for first time your string.count will be 1 but your textfeild.text.count will be 0.
Looking at your validation code I will suggest you add an IBAction on your textfeild for editing changed[here].
What does your validation function do? Why are you returning true by default?
shouldChangeCharactersIn should return the result of your validation function.

checking validity of decimal input in UITextfield

One of the UITextfield input should accept decimals. But I want to restrict the user to type only one ".", otherwise the floating point conversion gets messed up if it has more than one "." in the number. How to ensure user does not key in more than one "." as a part of decimal input?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if ( textField == areaTextField) {
guard CharacterSet(charactersIn: "0123456789.").isSuperset(of: CharacterSet(charactersIn: string)) else {
return false
}
}
return true
}
Your approach is wrong to start with; you are way overthinking this. The computer already knows whether a string is a valid number, so just ask it. Don't examine any characters. Don't examine any keys. Simply perform the replacement described by the parameters and ask the computer if the result would be a number:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let rep = (textField.text! as NSString).replacingCharacters(in: range, with: string)
return rep.count == 0 || Double(rep) != nil
}

How to properly solve the EXC_BAD_ACCESS (code=EXC_i386_GPFLT) error in my function

Ok, so total Swift noob here. Going through some beginner tutorials.
In order to achieve some goal,
I need a UITextField to accept only numbers
I have managed to get the UITextField to accept only letters via this code:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.TextField.delegate = self
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let allowedCharacters = CharacterSet.letters
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
}
However as soon as I turn allowedCharacters into something like:
let allowedCharacters = CharacterSet(charactersIn: "0123456789")
The Simulator still launches successfully (and I get a Build Succeeded message)
However, as soon as I start typing it crashes. Xcode then highlights my return statement and shows the error message:
Thread 1: EXC_BAD_ACCESS (code=EXC_i386_GPFLT)
When I google for it I get the sense that it might have something to do with the fact that I might need to an if-statement somewhere in my function to catch any unexpected values that I guess are being returned as soon as I start typing in the textfield, but I can not see the logic yet as to where and how.
Any thoughts to enlighten me would be great!
PS: I know I could also just configure my textfield to only show a numpad, but I'd still like to know why the current solution is causing problems for I might need other, more advanced, restrictions sometime in the future.
EDIT
Another solution that works (which I discovered after posting the question) is to change my declaration of allowedCharacters as such:
let allowedCharacters = CharacterSet.decimalDigits
However, the accepted answer to this question allows for more finetuning when necessary.
If you want to handle it that way, instead of making a character set out of your string and checking if it's a subset you should use string's method rangeOfCharacter(from: CharacterSet) -> Range<String.Index>?. This method returns a position of a character from given set or nil if there was no character from the set present.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print(string)
let allowedCharacters = CharacterSet(charactersIn: "0123456789")
return string.rangeOfCharacter(from: allowedCharacters) != nil
}
However it is not a good approach to specify allowed characters as here you'll prevent user from removing characters with backspace. You should rather specify disallowed characters then this function will change to:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print(string)
let disallowedCharacters = CharacterSet(charactersIn: "0123456789")
return string.rangeOfCharacter(from: disallowedCharacters) == nil
}

Detecting if the user is typing?

For a UITextField, what is the best way to detect if the user is typing and the textfield has a value?
I've tried the following unsuccessfully:
Value Changed: no response
Editing Did End: no response
Touch Up Inside: doesn't trigger until after the user clicks out of the textfield
Try using the shouldChangeCharactersInRange event.
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool {
// Put your code here
}
This is called whenever a user adds or removes a new character to your UITextField.
If you want to accept the change, return true. Otherwise return false.
Just make sure your UITextField conforms to the UITextFieldDelegate Protocol