I am writing validation for my textfield, I found something interesting that whether I can check how many digits I am typing into the textfield at real time. My text field input must be 8 digit number. So I want to change the text inside the text field to green colour when I reach 8 digit and change colour when it's not.
How can I do that? Please help me, thanks in advance.
Using -textField:shouldChangeCharactersInRange:replacementString: is probably a bad solution because it fires before the text field updates. This method should probably be used when you want to change the text of the text field before the keyboard automatically updates it. Here, you probably want to simply use target-action pairing when editing value changes:
[textField addTarget:self action:#selector(checkTextField:) forControlEvents:UIControlEventEditingChanged];
Then, in - (void)checkTextField:(id)sender, try this:
UITextField *textField = (UITextField *)sender;
if ([textField.text length] == 8) {
textField.textColor = [UIColor greenColor]; // No cargo-culting please, this color is very ugly...
} else {
textField.textColor = [UIColor blackColor];
/* Must be done in case the user deletes a key after adding 8 digits,
or adds a ninth digit */
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * searchStr = [textField.text stringByReplacingCharactersInRange:range withString:string];
// [textField2 setText:[textField1.text stringByReplacingCharactersInRange:range withString:string]];
NSLog(#"%#",searchStr);
return YES;
}
Use UITextFieldDelegate. especially this function
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
and you can get sample codes links from here..
[textField addTarget:self action:#selector(checkTextField) forControlEvents:UIControlEventEditingChanged];
Try it!
Set up a delegate for the UITextField and implement the method – textField:shouldChangeCharactersInRange:replacementString: Details and examples are in the UITextFieldDelegate Protocol Reference, but here's a quick example:
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
NSString *text = [textField text];
// NOT BACKSPACE
if ([string length] || text.length + string.length < 8) {
return YES;
} else if (text.length + string.length > 8) {
return NO;
} else {
// DO SOMETHING FOR LENGTH == 8
return YES;
}
}
Update Swift 4.0
txtFieldAdd.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
Now call your function. In my case, this will enable the save button
#objc func textFieldDidChange(_ textField: UITextField) {
if (txtFieldAdd.text?.isEmpty)! {
btnSave.isEnabled = false
}
else {
btnSave.isEnabled = true
}
}
Hope it helps.
This is how you get realtime validation without setting up anything other than the delegate of the textfield:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// Creating the future string. The incoming string can be a single character,
// empty (on delete) or many characters when the user is pasting text.
let futureString: String = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
// Determine if the change is allowed, update form, etc.
let allowChange = <#Your Validation Code#>
return allowChange
}
Add this code to the designated delegate of the textfield. Simple.
Related
In my application i have one UILabel and UITextField. Initially UILabel text in nil.
As soon as user enter some text in UITextField my UILabel text also Update.
Let say When user enter A in UITextField my UILabel immediately show A, B in UITextField my UILabel show B and so on.
To achieve this behaviour i used the shouldChangeCharactersInRange function of UITextFieldDelegate. But it always behind one character. say UITextField text = qwerty my UIlabel text show qwert
Please help me so i can continuously update the UILabel text as user enter value in UITextField
here is my code
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
mylabel.text = textfield.text
return true
}
You can register your textField for value change event:
[textField addTarget: self action:#selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
and in textFieldDidChange function update your label:
- (void)textFieldDidChange
{
label.text = textField.text;
}
The function shouldChangeCharactersInRange is needed more for taking desisions whether to allow upcoming change or not
You can use following code to change the text:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
myLabel.text = [textField.text stringByReplacingCharactersInRange:range withString:string]
return YES;
}
In Swift
Register your edit events
textfield.addTarget(self, action:"changeLabel", forControlEvents:UIControlEvents.EditingChanged)
func changeLabel(){
myLabel.text=textfield.text
}
you can use this code easy to understand :
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *str = [NSString stringWithFormat:#"%#%#",yourtextfield.text,string];
NSLog(#"%#",str);
return YES;
}
I have one UiTextField Called MobileNumber. and two pickerView called Operator and circle. When I enter first 4 digits of my number in textfield it displays the Operator pickerview,
how to call it when I enter first 4 digits value in textfield and display the pickerview
Use shouldChangeCharactersInRange textField's delegate method for entering numeric character
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([string rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]].location != NSNotFound)
{
//This field accepts only numeric entries.
return NO;
}
else //numeric value entered
{
NSString *strTxtField = [textField.text stringByAppendingString:string];
if(strTxtField.length == 4)
{
//show pickerview here
//Set return NO if you don't want more character to be added to textfield
//return NO;
}
return YES;
}
}
You can declare a notification that will be called on every single change in text field.
// Add a "textFieldDidChange" notification method to the text field control.
[textField addTarget:self
action:#selector(textFieldDidChange:)
forControlEvents:UIControlEventEditingChanged];
Now here on this method textFieldDidChange: you can access your text field and check if user has entered 4 digits you can show the picker.
try this code...
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(newString.length == 4)
{
NSLog(#"show date picker");
//Set return NO if you don't want more character to be added to textfield
//return NO;
}
return newString.length <= 4;
}
make sure that your text field delegate set properly..
I have subclassed UITextField and implemented the UIKeyInput protocol's deleteBackward method to detect backspace being pressed. This works fine on iOS 7 but not on iOS 8.
deleteBackward is not called on the UITextField anymore when I press the backspace key.
I've checked the documentation and the release notes and nothing points to the reason why this could happen. Any pointers?
A lot of people have been saying this is a bug, but being that this problem still exists in the GM I'm starting to think it might be a change in logic. With that said, I wrote this bit of code for my app and have tested it on iOS 7-8.
Add the following method to your UITextField subclass.
- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
BOOL shouldDelete = YES;
if ([UITextField instancesRespondToSelector:_cmd]) {
BOOL (*keyboardInputShouldDelete)(id, SEL, UITextField *) = (BOOL (*)(id, SEL, UITextField *))[UITextField instanceMethodForSelector:_cmd];
if (keyboardInputShouldDelete) {
shouldDelete = keyboardInputShouldDelete(self, _cmd, textField);
}
}
BOOL isIos8 = ([[[UIDevice currentDevice] systemVersion] intValue] == 8);
BOOL isLessThanIos8_3 = ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.3f);
if (![textField.text length] && isIos8 && isLessThanIos8_3) {
[self deleteBackward];
}
return shouldDelete;
}
This code is slightly before the red line of private API's, however you should have no problem using it. My app with this code is in the app store.
To explain a little, were calling the super implementation of this method to avoid losing code. After were going to call -deleteBackward if there is no text and the iOS version is between 8-8.2.
EDIT: 1/22/15
It also might be helpful to subclass the -deleteBackward method of your subclassed UITextField. This fixes a few conditional bugs. One being if you use a custom keyboard. Heres an example of the method.
- (void)deleteBackward {
BOOL shouldDismiss = [self.text length] == 0;
[super deleteBackward];
if (shouldDismiss) {
if ([self.delegate respondsToSelector:#selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
[self.delegate textField:self shouldChangeCharactersInRange:NSMakeRange(0, 0) replacementString:#""];
}
}
}
EDIT: 4/13/15
As #Gee.E commented, iOS 8.3 has fixed this issue. The code has been updated to reflect the changes.
You can detect when user deletes text by using backspace by implementing UITextField delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (range.length==1 && string.length==0)
NSLog(#"backspace tapped");
return YES;
}
You must look an example for MBContactPicker on github. Deletion of contacts at MBContactPicker via Backspace button on iOS8 tested by me. And it works greatly! You can use its as example.
Author of MBContactPicker use next method: When UITextField must become empty (or before call becomeFirstResponder when it is empty), he save single whitespace symbol there. And then when you press Backspace button (when focus was set to end of text of your UITextField), method
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
will work. Inside it you must use check like this:
NSString *resultString = [textField.text stringByReplacingCharactersInRange:range withString:string];
BOOL isPressedBackspaceAfterSingleSpaceSymbol = [string isEqualToString:#""] && [resultString isEqualToString:#""] && range.location == 0 && range.length == 1;
if (isPressedBackspaceAfterSingleSpaceSymbol) {
// your actions for deleteBackward actions
}
So, you must always control that UITextField contains single whitespace.
This is not hack. So, user willn't noticed about some behaviour was changed
Swift 2.2:
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text == "" {
print("Backspace has been pressed")
}
return true
}
In iOS8, some custom keyboards delete whole word, so only check string.length is OK.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (string.length==0) { //Delete any cases
if(range.length > 1){
//Delete whole word
}
else if(range.length == 1){
//Delete single letter
}
else if(range.length == 0){
//Tap delete key when textField empty
}
}
return YES;
}
This does not explicitly answer the original question but worth nothing that in the documentation for textField(_:shouldChangeCharactersIn:replacementString:), it says:
"string: The replacement string for the specified range. During typing, this parameter normally contains only the single new character that was typed, but it may contain more characters if the user is pasting text. When the user deletes one or more characters, the replacement string is empty."
Thus, we can detect backspaces in a UITextFieldDelegate if we implement textField(_:shouldChangeCharactersIn:replacementString:) and check if the length of string is 0.
A lot of other answers here have used this same logic without referencing the documentation so hopefully getting it right from the source makes people more comfortable using it.
Swift 2.0 version for Detecting BackSpace based deletion, referencing code post from almas
//For Detecting Backspace based Deletion of Entire Word in TextField
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if (range.length == 1 && string.isEmpty){
print("Used Backspace")
}
return true
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
const char * _char = [string cStringUsingEncoding:NSUTF8StringEncoding];
int isBackSpace = strcmp(_char, "\b");
if (isBackSpace == -8) {
NSLog(#"Backspace was pressed");
}
return YES;
}
Basically this method detects which button you are pressing (or have just pressed). This input comes in as an NSString. We convert this NSString to a C char type and then compare it to the traditional backspace character (\b). Then if this strcmp is equal to -8, we can detect it as a backspace.
swift 2:
if (string.characters.count == 0 && range.length == 1) {
return true
}
you should use like this string.characters.count
func keyboardInputShouldDelete(_ textField: UITextField) -> Bool {
}
This function is called when you hit delete key
I have a text field where I want to insert a phone number. The problem is that I use number pad and there is not any enter key and I without that key I don't know how to close the text field.
Is there any way of checking how many number os characters there are in a text field and close the number pad when there are X chars?
Sorry for my english and sorry for the noob question but I am just starting with ObjC.
Thanks *
Sorry guys, I forgot to tell you that I already have tried this:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField.text.length== 9) {
[textField resignFirstResponder];
}
return YES;
}
Yes, you have two choices in my opinion, you can:
A. Check the number of chars returned in delegate method shouldChangeTextInRange and then if your limit is reached resign first responder (when you resign first responder the keyboard dismisses,
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if ( textView.text.length + (text.length - range.length) == 10) //or whatever value you like
{
[textField resignFirstResponder];
}
}
Or B:
Override the touchesEnded method of your view and call resignFirstResponder, so that when the user touches the view outside the textField, the keyboard dismisses. - this is what I do in my app.
Like this:
Just add this method to your viewController, it will be called automatically when the touch in the view ends, in this method you simply resignFirstResponder and the keyboard will disappear.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[textField resignFirstResponder];
}
I think it's better to do this in touchesEnded than began, it 'feels' nicer to have the keyboard dismiss when you lift your finger from the view, rather than when to initially touch it.
I found that resignFirstResponder do not include the last char typed. I had to do it in this way,
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
DLog(#"Existing: %#, string: %#, range:(%lu, %lu)", textField.text, string, (unsigned long)range.location, (unsigned long)range.length);
if (textField == field2.textField) {
if ( textField.text.length + (string.length - range.length) == kMyNumberLength) // e.g. 10
{
textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
[textField resignFirstResponder];
}
if (textField.text.length + string.length - range.length > kMyNumberLength) {
return NO;
} else {
return YES;
}
}
return YES;
}
Swift
when reaching the maximum number of characters, add the new char to the current text in the textfield and then dismiss the keyboard
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let maxLength = 5
let currentString: NSString = textField.text! as NSString
let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
if newString.length == maxLength {
textField.text = textField.text! + string
textField.resignFirstResponder()
}
return newString.length <= maxLength
}
Every time a user adds a number, the delegate of your text field will be notified through this method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger length = [[textField text] length] - range.length + string.length;
if (length == ...) ...
}
You can set up your delegate in such a way as to watch the length of the modified content of your text field, and close the field when the expected length is reached.
However, this has a high potential to frustrate your users a lot: they would make a mistake entering the last character every now and then, and the field is going to close on them, not letting them correct the problem. A better approach is to dismiss the pad when users tap away from your text field, which keeps end-users in control of what is going on:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[textField resignFirstResponder];
}
You can just put a toolbar with a Done on it, on the click on that button you can hide keyboard and toolbar as well.
-(IBAction)doneButtonClicked:(id)sender{
[yourTextField resignFirstResponder];
[toolBar setHidden:YES];
}
or you can use this custom tool
Had issues with some of the suggestions above not retaining the last character entered.
I found this worked well for me.
Method simply examines the length of the text as it is building up and then runs [textField resignFirstResponder] to dismiss.
All usual textField delegates will run for the textField at point field loses focus and keyboard is dismissed.
- (void)textFieldDidChangeSelection:(UITextField *)textField {
NSLog(#"textField.text: %#", textField.text);
if (textField.text.length == 4) // If 4 is your max-length
[textField resignFirstResponder];
}
Use this textfield delegate method to find out the length of textField.text
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
int textLength = [textField.text length] + 1;
return YES;
}
I need to identify the deleted character. For example, when I delete a character "#", I need to perform some actions. Is there any easy way to do this? Or do I need to keep the recently typed character in a variable and check the range.length == 0 in shouldChangeCharactersInRange?
Thanks in advance.
In my opinion, it should be just like this:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *textToChange = [[textField text] substringWithRange:range];
NSRange rangeOld = [textToChange rangeOfString:#"#"];
NSRange rangeNew = [string rangeOfString:#"#"];
if (rangeOld.location != NSNotFound && rangeNew.location == NSNotFound ) {
[self userDidRemoveAtSign];
}
return YES;
}
Explanation: Yep, userDidRemoveAtSign is your custom method when user deletes '#' sign. This code resides in UITextField delegate. Every time user change text in UITextField, delete, replace or appent characters, textfield send this message
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
to delegate. In this method I check if # sign is cotained in replaced text, and if it is contained in replacement.
Yes as you said you will have to keep the last set of characters in a NSString and then when text changes compare it with the new string
You can do this in swift by this way, you will get deleted character
let strDeletedChar = (textView.text as NSString).substringWithRange(range)
You can catch the deletede char in a method of UITextView Delegate:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
NSString *deletedChar = [textView.text substringWithRange:range];
if ([deletedChar isEqualToString:#"#"]) {
// do something
return YES;
}else{
// do something
return YES;
}
}
UITextView Delegate Swift 5 version:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let txt = textView.text, let txtRange = Range(range, in: txt) else {
return false
}
let subString: Substring = txt[txtRange]
if subString == "(searchString)" {
// do something
return true
}
}