UITextField check for input - ios

I have in my TableViewCell 2 TextFields.
For the 2nd TextField i need a confirm to enable a button.
I´m using this code to determine if the confirm button should be enabled, but it doesn't work:
-(BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
textField = self.installDeviceCell.passWordTextFieldOutlet;
[textField addTarget:selfaction:#selector(checkTextField:)forControlEvents:UIControlEventEditingChanged];
return YES;
}
-(void)checkTextField:(id)sender
{
UITextField* textField = (UITextField*)sender;
if ([textField.text length] < 7) {
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = NO;
}else if ([textField.text length] > 7){
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = YES;
}else{
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = YES;
}
}

Have you assigned your UITextFieldDelegate for your UITextField when it is being instatiated? If don't assign the it the delegate methods for the UITextField will not be called.
Also make sure that in the header file of the delegate you specify the delegate of the interface e.g.
#interface MyDelegate <UITextFieldDelegate>
// ...
#end
Finally you're doing your text calculations a little oddly. You can do all your computations witin shouldChangeCharactersInRange. You also don't need to add the target or overwrite the textField property. Simply check if the replacementString is greater than 7 characters. Your final code might look something like this:
-(BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([string length] < 7) {
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = NO;
} else {
self.installDeviceCell.installDeviceTouchUpInsideButton.enabled = YES;
}
return YES;
}
See this great answer on delegation for more info: https://stackoverflow.com/a/626946/740474

Related

What is the correct way to change keyboard return button for UITextView

So I have been working on a small requirement that has taken me way more time than I would like, as small requirements in UIKit seem to do sometimes:
When a user enters a password longer than 3 characters you change the
keyboard to have a done button.
Simple enough... it seems that KVO isn't fired until editing ends, and neither is the textFieldDidEndEditing: delegate method called. Ok, easy enough, just do our logic in the -(BOOL)textField:shouldChangeCharactersInRange:replacementString: delegate callback...
Attempt A:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
}
return YES;
}
Attempt A, does nothing... never changes the keyboard to Go
Attempt B:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
[textField resignFirstResponder];
[textField becomeFirstResponder];
}
return YES;
}
Attempt B: yay, keyboard button changes, but when we resignFirstResponder, we discard the new input so the User can't enter their password... bad
Attempt C:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
}else{
textField.returnKeyType = UIReturnKeyDefault;
}
textField.text = newString;
[textField resignFirstResponder];
[textField becomeFirstResponder];
return NO;
}
Attempt C is tricky, we return NO, telling the delegate not to accept the edit, but that is ok, because we explicitly set the string in the delegate (this seems like sort of a bad idea), but it all works, except that when you resign firstResponder status it changes your keyboard (if you had the number keyboard up, it will switch to default after every keystroke)
Attempt D:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString * newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length >=3)
{
textField.returnKeyType = UIReturnKeyGo;
}else{
textField.returnKeyType = UIReturnKeyDefault;
}
//detect if we have crossed a boundry
if ((textField.text.length >=3) != (newString.length >=3))
{
[textField resignFirstResponder];
[textField becomeFirstResponder];
}
textField.text = newString;
return NO;
}
Attempt D is pretty good, it will only resign first responder as you cross the 2/3 or 3/2 edge so you only lose your keyboard once, not a big deal usually
so the question, what is the best practice way to do this?
(resigning first responder only seems to cancel the edit if using secure input, if you aren't familiar with this problem), I have also prepared a sample project, as to help anyone that wants to look at it: sample project
That's far too much work. Just make yourself the delegate of the UITextView and you will get TextViewDidChange messages.
- (void)textViewDidChange:(UITextView *)textView;
Or if using a UITextField register for its UITextFieldTextDidChangeNotification message.
add the delegate on text did change
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
-(void)textFieldDidChange :(UITextField *)theTextField
{
enter your logic here
}
Try this
textField.returnKeyType = UIReturnKeyNext;

Set max length of UITextField AND convert to uppercase?

I have a Client Details screen with many UITextField. I need to limit the postcodeField to a maximum of 7 characters and convert to uppercase automatically. I already have code to convert the text to uppercase, but it seems I cannot do anything else with that particular UITextField in its Delegatemethod
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
Here is what I have tried:
#define MAXLENGTH 7
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField == self.postcodeField) {
self.postcodeField.text = [textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]];
return NO;
}
if (self.postcodeField.text.length >= MAXLENGTH && range.length == 0)
{
return NO;
}
return YES;
}
And:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (textField == self.postcodeField) {
self.postcodeField.text = [textField.text stringByReplacingCharactersInRange:range withString:[string uppercaseString]];
return NO;
}
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 7) ? NO : YES;
}
This code does not work. I know there are many threads with various solutions to setting a maximum length, but I can't find a solution that caters for uppercase conversion too. I am quite new to iOS so I apologise if this is seen as a duplicate post. Any help is much appreciated!
This will surly help to restrict to 7 Characters.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
[textField setText:[textField.text uppercaseString]];
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > 7) ? NO : YES;
}
In my opinion the better approach to your problem is to use NSNotificationCenter with UITextFieldTextDidChangeNotification
Then you can add this code to viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(maxLength:)
name:UITextFieldTextDidChangeNotification
object:self.postcodeField];
then, you only need to add the selector method, e.g.:
- (void) maxLength: (NSNotification*) notification
{
UITextField *notificationTextField = [notification object];
if (notificationTextField == self.postcodeField)
{
if (self.postcodeField.text.length >= MAXLENGTH)
{
// remove here the extra text
}
}
}
I have run this code and its working fine, so try it out
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField==self.txtPostCodeField)
{
int limit = 100;
return !([textField.text length]>=limit && [string length] >= range.length);
}
return YES;
}
I have decided to implement a new method for achieving what I want. My UITextField delegate method - (BOOL)textField: shouldChangeCharactersInRange: replacementString: was getting very messy as more textfields were added to the view, all of which are doing different things. So I have created a subclass to use on the desired Postcode field. I wasn't able to use the posted solution in a subclass of UITextField as it is bad to set the delegate to self within that subclass (a known issue with UITextField subclassing - see this, this, and this.). The new code is more efficient than the answer I had previously accepted, and can be widely adapted to do many things.
The header file:
PostcodeField.h
#interface PostcodeField : UITextField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range;
#end
The subclass implementation (another requirement was to only accept specified characters which has been easily implemented):
PostcodeField.m
#define ACCEPTABLE_CHARACTERS #" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
#define CHARACTER_LIMIT 8
#implementation PostcodeField
- (BOOL)stringIsAcceptable:(NSString *)string inRange:(NSRange)range {
NSUInteger newLength = [self.text length] + [string length] - range.length;
// Check text meets character limit
if (newLength <= CHARACTER_LIMIT) {
// Convert characters to uppercase and return acceptable characters
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:ACCEPTABLE_CHARACTERS] invertedSet];
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:#""];
[self setText:[self.text stringByReplacingCharactersInRange:range withString:[filtered uppercaseString]]];
}
return NO;
}
Then I set the delegate to self on the desired textfield within it's ViewController:
self.postcodeField.delegate = self;
And call it's delegate method on the ViewController:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// Call PostcodeField subclass on Postcode textfield
if ([textField isKindOfClass:[PostcodeField class]]) {
return [(PostcodeField *)textField stringIsAcceptable:string inRange:range];
}
return YES;
}
And of course, importing the subclass on the ViewController:
#import "PostcodeField.h"
You can set the textfield to use a subclass by navigating to the "Identity Inspector" using IB (Interface Builder) and setting the Custom Class to your subclass:
By using subclasses, your UITextField delegate method can be cleaner and more efficient, and the subclass can be called on as many textfields as you like. If there are multiple textfields on that view, just follow the same process and test for each subclass within the UITextField delegate method. I hope this post will be helpful for anyone wanting to utilise subclasses on UITextFields.
In Swift you can use the code below to do it.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if(range.length + range.location > countElements(textField.text))
{
return false
}
textField.autocapitalizationType = UITextAutocapitalizationType.AllCharacters // Capitalize the all characters automatically
var newLength: Int = countElements(textField.text) + countElements(string) - range.length
return (newLength > 7) ? false : true
}

Setting TextField Focus on text removal

i have four textfields. User is allowed to enter only one digit in each text field.Once the user Enters single digit its focus should be on the next textfield. i have done this part and its working fine. Now, What i want is when i remove the text from the text field then its focus should move to previous textfield. i mean if i am deleting the text(digit) from the fourth text field then its focus should move to third text field. In short on removal of text the focus should be on previous textfield.
Now, what my problem is when i remove the text from the textfield then its focus moves to previous textfield but it clears the text of that textfield(the textfield on which i have set the focus).What i want is the text should not be removed on focus.
in .h file
IBOutlet UITextField *txtPinDigit1;
IBOutlet UITextField *txtPinDigit2;
IBOutlet UITextField *txtPinDigit3;
IBOutlet UITextField *txtPinDigit4;
UITextField *currentTextField;
in .m file
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if(textField.tag==0)
{
currentTextField=txtPinDigit1;
}
else if(textField.tag==1)
{
currentTextField=txtPinDigit2;
}
else if(textField.tag==2)
{
currentTextField=txtPinDigit3;
if(isDelete)
{
textField.text=digit3;
}
}
else if(textField.tag==3)
{
currentTextField=txtPinDigit4;
}
return YES;
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(([textField.text length]==1)&&(![string isEqualToString:#""]))
{
if(currentTextField==txtPinDigit1)
{
[txtPinDigit2 becomeFirstResponder];
}
else if(currentTextField==txtPinDigit2)
{
[txtPinDigit3 becomeFirstResponder];
}
else if(currentTextField==txtPinDigit3)
{
[txtPinDigit4 becomeFirstResponder];
}
else if(currentTextField==txtPinDigit4)
{
textField.text = [textField.text substringToIndex:MAXLENGTH-1];
//[txtPinDigit4 resignFirstResponder];
//[txtPinDigit1 becomeFirstResponder];
}
}
else if([string isEqualToString:#""])
{
isDelete=YES;
NSLog(#"replacementString:%#",string);
// textField.text=string;
if(currentTextField==txtPinDigit4)
{
textField.text=string;
digit3=nil;
[digit3 release];
digit3=[[NSString alloc] initWithFormat:#"%i",[txtPinDigit3.text intValue]];
[txtPinDigit3 becomeFirstResponder];
}
else if(currentTextField==txtPinDigit3)
{
textField.text=string;
[txtPinDigit2 becomeFirstResponder];
}
else if(currentTextField==txtPinDigit2)
{
textField.text=string;
[txtPinDigit1 becomeFirstResponder];
}
else if(currentTextField==txtPinDigit1)
{
textField.text=string;
// [txtPinDigit1 resignFirstResponder];
//[txtPinDigit1 becomeFirstResponder];
}
}
return YES;
}
in the above code MAXLENGTH=1 defined.
any help will be appreciated.
thanks in advance.
Try This code, Change shouldChangeCharactersInRange: Delegate Method
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if ([newString length] < 1)
{
if (textField.tag==2)
{
[txtPinDigit1 performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
// txt2.text=#"";
}
else if (textField.tag==3)
{
[txtPinDigit2 performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
}
else if (textField.tag==4)
{
[txtPinDigit3 performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
}
return YES;
} else
{
// Otherwise we cut the length of newString to 1 (if needed) and set it to the textField.
textField.text = [newString length] > 1 ? [newString substringToIndex:1] : newString;
if (textField.tag==1)
{
[textField resignFirstResponder];
[txtPinDigit2 becomeFirstResponder];
}
else if (textField.tag==2)
{
[textField resignFirstResponder];
[txtPinDigit3 becomeFirstResponder];
}
else if (textField.tag==3)
{
[textField resignFirstResponder];
[txtPinDigit4 becomeFirstResponder];
}
return NO;
}
}
Check to check length via this way and put a break point at beginning of shouldChangeCharactersInRange
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
int length = textField.text.length - range.length + string.length;
return YES;
}
I hope it will help you.
Before calling up to become first responder on filling up the 1st digit,bind the entered text inputs & the responder into dictionary...
On each previous/next condition,retrieve the information from the dictionary...The below example code is for the general previous/next custom actions(not auto though)..You just need to change the logic a bit based on the condition you prefer...
- (NSArray *) responders
{
if (_responders)
return _responders;
NSArray *textInputs = EditableTextInputsInView([[UIApplication sharedApplication] keyWindow]);
return [textInputs sortedArrayUsingComparator:^NSComparisonResult(UIView *textInput1, UIView *textInput2) {
UIView *commonAncestorView = textInput1.superview;
while (commonAncestorView && ![textInput2 isDescendantOfView:commonAncestorView])
commonAncestorView = commonAncestorView.superview;
CGRect frame1 = [textInput1 convertRect:textInput1.bounds toView:commonAncestorView];
CGRect frame2 = [textInput2 convertRect:textInput2.bounds toView:commonAncestorView];
return [#(CGRectGetMinY(frame1)) compare:#(CGRectGetMinY(frame2))];
}];
}
The below function should get called before doing previous/next auto tab.
- (Void) selectAdjacentResponder: (UISegmentedControl *) sender
{
NSArray *firstResponders = [self.responders filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(UIResponder *responder, NSDictionary *bindings) {
return [responder isFirstResponder];
}]];
NSLog(#"%#",firstResponders);
UIResponder *firstResponder = [firstResponders lastObject];
NSInteger offset = sender.selectedSegmentIndex == 0 ? -1 : +1;
NSInteger firstResponderIndex = [self.responders indexOfObject:firstResponder];
NSInteger adjacentResponderIndex = firstResponderIndex != NSNotFound ? firstResponderIndex + offset : NSNotFound;
UIResponder *adjacentResponder = nil;
if (adjacentResponderIndex >= 0 && adjacentResponderIndex < (NSInteger)[self.responders count])
adjacentResponder = [self.responders objectAtIndex:adjacentResponderIndex];
[adjacentResponder becomeFirstResponder];
}
Bit lengthier,but this is all i know a BIT :)..Happy coding...

UItextfields autofocus

i have 6 textfields named digit1,digit2..etc upto digit6 added as a subview over a view. i want the digit2 textfield to autofocus once the user enters a digit in the digit1 textfield and similarly digit3 should autofocus when a digit is entered in digit2 textfield.Below shown is the code i tried.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField.text.length>=1)
{
[textField resignFirstResponder];
UITextField *_textField=(UITextField*) [self.view viewWithTag:textField.tag+1];
[_textField becomeFirstResponder];
}
return TRUE;
}
What happens here is when i enter a digit in digit1 it dosent appear in digit 1 but it appears in digit2 and also when i click delete button the control is transfered to the subsequent textfields rather than deleting the text in the current textfield.Please help me to fix this.
lets assume that you have 3 textfields with names such as t1, t2 and t3
set tag value for t1 = 1, t2 = 3 and t3 = 3
set tag values for all the textfields and the write this code
then write this code
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
switch(textField.tag){
case 1:{
if(textField.text.length>=1)
{
[t1 resignFirstResponder];
[t2 becomeFirstResponder];
break;
}
}
case 2:{
if(textField.text.length>=1)
{
[t2 resignFirstResponder];
[t3 becomeFirstResponder];
break;
}
}
case 3:{
if(textField.text.length>=1)
{
[t3 resignFirstResponder];
break;
}
}
}
}
return TRUE;
}
I think the problem is that shouldChangeCharactersInRange gets called before the edit actually occurs. Since you are changing the first responder in there, the edit takes effect in the new first responder instead of the old one.
You could try moving the code inside your if block to another method, and calling it with [self performselector:withObject:afterDelay], setting the delay to some small value. Something like:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if(textField.text.length>=1)
{
[self performSelector:#selector(switchTextField) withObject:nil afterDelay:0.1];
}
return TRUE;
}
-(void)switchTextField
{
[textField resignFirstResponder];
UITextField *_textField=(UITextField*) [self.view viewWithTag:textField.tag+1];
[_textField becomeFirstResponder];
}
That way shouldChangeCharactersInRange returns true immediately (as it should), the edit will take place in the currently selected textField, and then your switchTextField method will get called soon after. It's not the cleanest solution, but it's the quickest I can think of off the top of my head.
I found the solution
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *str = [textField.text stringByReplacingCharactersInRange:range withString:string];
if( [str length] > 0 ){
textField.text = string;
UIResponder* nextResponder = [textField.superview viewWithTag:(textField.tag + 1)];
if (nextResponder) {
[nextResponder becomeFirstResponder];
}
return NO;
}
return YES;
}

Enabling button if I write in TextField

I am having some problems with enabling a button when you write something in a TextField.
I got it to disable if a there's nothing in the TextField but I can't make it to enable again when you write something in it.
I have something like this:
- (void)viewDidLoad {
[super viewDidLoad];
NSUInteger textLength = [_Name.text length];
[_doneButton setEnabled:(textLength > 0)];
}
Set delegate to the UITextField and create a method for text change in ViewDidLoad as follows.
[self.textFieldTemp addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
And add the method as follows.
- (void)textFieldDidChange:(UITextField *)textField {
if(textField == self.textFieldTemp)
self.buttonTemp.enabled = ([self.textFieldTemp.text length] > 0) ? YES : NO; }
Try this
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if([textField.text length]>0){
[doneButton setEnabled:YES];
}
else{
[doneButton setEnabled:NO ;
}
Hope this May be help you..
Use UITextField delegate method to enable your button again. When your textField characters are changed, delegate method will be called, enable your button here.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
_doneButton.enabled = YES;
}
Put the lines in textfield delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
It'll work then. This method is called every time you input a character in textfield. But ViewDidLoad is called only when the viewcontroller is loaded.
Hope it will work for you.
These two functions will enable and disable your button according the text in your textfield
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
_doneButton.enabled = YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
if([textField.text length]>0){
[doneButton setEnabled:YES];
}
else{
[doneButton setEnabled:NO ;
}
You can use delegates of UITextField to enable and disable the button like this.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
{
if (textField.text.length!=0)
{
yourButton.enabled = YES;
}
else
{
yourButton.enabled = NO;
}
}
The method I used didn't use the delegates at all. I created this method in the controller class:
- (IBAction)textFieldChanged:(id)sender
{
self.doneButton.enabled = ([self.Name.text length] > 0) ? YES : NO;
}
Then, in the Interface Builder, I set the text field "Editing Changed" action to target the new method. Then, similar to #Paramasivan Samuttiram, I added an extra call to it in the viewDidLoad:.
[self textFieldChanged:self.Name];
That part allows the button to be in the correct state initially.

Resources