Reputation: 14549
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
Upvotes: 0
Views: 556
Reputation: 2459
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.
Upvotes: 1
Reputation: 3512
add the delegate on text did change
[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
-(void)textFieldDidChange :(UITextField *)theTextField
{
enter your logic here
}
Upvotes: 0