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;
}
Related
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;
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
The next code will work only when the user will enter the third character. I want the focus to move TextField when the user enters the second character but still keep the first 2 characters in the first TextField. if I'll try something likenewString.length < 2 , when entering 2 characters in a row I'll get the first character in the first UITextField and the second character in the second UITextField.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length <= 2 )
{
return YES;
}
else
{
NSInteger nextTag;
nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
}
else {
[textField resignFirstResponder];
}
}
return YES;
}
I belive this should work:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if(newString.length == 2)
{
UIResponder* nextResponder = [textField.superview viewWithTag:textField.tag + 1];
if (nextResponder) {
[nextResponder performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.1];
}
else {
[textField resignFirstResponder];
}
}
return newString.length <= 2;
}
Please try it and tell me if it works. If not please tell me a bit more about your setup and I will try to set up a view like yours and it will be easier for me to try solutions.
Ok, EDIT The above works. It is not very beautiful but it does the job. It is sad that apple would not add a method for didChangeCharactersInRange which would be much more appropriate for this.
EDIT Edited according to Sha's findings 0.1 instead of 0 in the timer interval
EDIT Changed according to Mike's suggestion
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...
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.