UITextFieldDelegate, checking if textFields have text in it - ios

i have a little problem.
There are two textFields in my TableView which set the property of an object. In order to do so i want to force the user to write something in the textField before the string is actually been set to the object. So basically a simple ([textField.text length] > 0) thing.
But i want that the user have to write strings in both the two textFields to finally enable the "Done"-Button.
I solved this earlier but with only one text Field with the following UITextFieldDelegate method.
- (BOOL)textField:(UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newText = [theTextField.text stringByReplacingCharactersInRange:range withString:string];
self.doneBarButton.enabled = ([newText length] > 0);
return YES;
}
My solution for the new problem, so now with two textFields is this one:
- (BOOL)textField:(UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newText = [theTextField.text stringByReplacingCharactersInRange:range withString:string];
if ([theTextField.placeholder isEqualToString:#"textField1"]) {
if ([theTextField.text length] > 0) {
enabledVokabel = YES;
} else {
enabledVokabel = NO;
}
}
if ([theTextField.placeholder isEqualToString:#"textField2"]) {
if ([theTextField.text length] > 0) {
enabledUebersetung = YES;
} else {
enabledUebersetung = NO;
}
}
self.doneBarButton.enabled = (enabledVokabel && enabledUebersetung);
return YES;
}
So i want the doneBarButton been enabled when both of the textFields (textField1 and textField2) are filled with text. But i want it that way that if the user has deleted the text he/she just wrote in the doneBarButton is disabled as soon as the textFields are empty.
It doesn't work that way. Do you have a solution? Or maybe a better way to solve it?

Either just connect value changed in interfacebuilder to a IBAction method in any of the classes you have in your view. Or you can do it in code with:
[textField addTarget:self
action:#selector(myIBActionMethod:)
forControlEvents:UIControlEventEditingChanged];
And check the length of the input.
You can of hook up both textfields to the same method and check the length of both textfields every time its called if you have IBOutlets to them both.

I'd keep a reference for both UITextViews, lets say.-
IBOutlet UITextView *textView1;
IBOutlet UITextView *textView2;
properly linked to your xib/storyboards. Also, I'd rather use
- (void)textViewDidChange:(UITextView *)textView
callback. According to shouldChangeCharactersInRange docs, it looks like this method is called before actually changing the text.
As for the enabled condition, it would look something like this.-
self.doneBarButton.enabled = [textView1.text length] > 0 && [textView2.text length] > 0;

Related

VENTokenField hold backspace key to delete tokens

While trying to allow multi token deletions, as user holds the backspace key in VENTokenField to act the same as the native email app, or messages app, I have come across many problems...
First, I can only detect one tap on the backspace key from the initial code the VENToken's UITextField subclass offer (which is technically touching private API) - (BOOL)keyboardInputShouldDelete:(UITextField *)textField. That is fine, but not helpful for detecting long press on backspace button, which only works while you actually have characters in a certain UITextField, and not while the UITextField is empty such as in our case.
I have also came across this blogpost which suggest another approach of accessing more of the private API, however, does not offer solution to my problem. As it's not documented, I was wondering if there is a valid way to detect this event at all?
I've resolved it by first, comment out anything that was in VENBackspaceTextField class'
keyboardInputShouldDelete:(UITextField *)textField
Then, added 2 consts in VENTokenField header:
NSString * const kTextEmpty = #"\u200B"; // Zero-Width Space
NSString * const kTextHidden = #"\u200D"; // Zero-Width Joiner
Everytime the token becomes first responder, make sure the textField has the empty text:
- (void)inputTextFieldBecomeFirstResponder {
[self.inputTextField becomeFirstResponder];
if (self.tokens.count) {
[self.inputTextField setText:kTextEmpty];
}
...
}
And set it to hidden when cursor is not visible:
- (void)setCursorVisibility {
NSArray *highlightedTokens = [self.tokens filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(VENToken *evaluatedObject, NSDictionary *bindings) {
return evaluatedObject.highlighted;
}]];
BOOL visible = [highlightedTokens count] == 0;
if (visible) {
[self inputTextFieldBecomeFirstResponder];
} else {
[self.invisibleTextField becomeFirstResponder];
[self.invisibleTextField setText:kTextHidden];
}
}
Then, modified the textField Delegate method:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (self.tokens.count && [string isEqualToString:#""] && [textField.text isEqualToString:kTextEmpty]){
VENToken *lastToken = [self.tokens lastObject];
lastToken.highlighted = YES;
[_inputTextField setText:kTextHidden];
_inputTextField.alpha = 0.0;
return NO;
}
if ([textField.text isEqualToString:kTextHidden]){
[self deleteHighlighted];
[self unhighlightAllTokens];
return (![string isEqualToString:#""]);
}
//If there are any highlighted tokens, delete
[self deleteHighlighted];
return YES;
}

Can I press the return key using code?

I have a uitextfield and I count the number of characters. The idea is that when the count reaches four then it should go on to the next textfield. The problem is that while the counter tells me that the field does contain four characters the field only shows three characters. It works when I manually press the return key but I don't the user to have to do this. Here's my code.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSInteger textLength = 0;
textLength = [textField.text length] + [string length] - range.length;
NSLog(#"Length: %ld", (long)textLength);
NSLog(#"tag: %ld", (long)textField.tag);
if (textField.tag == 1 || textField.tag == 2) {
if (textLength == 4) {
NSLog(#"doneeee");
NSLog(#"testfield: %#", textField.text);
}
}
if (textField.tag == 3) {
NSLog(#"we're here");
if (textLength == 6) {
NSLog(#"Zip is done");
[self checkTheTextField:textField];
}
}
return YES;
}
As another option, when the length hits 4 you can just set the textField.text directly, set the next textField as first responder, and return NO.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *text = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (textField.tag == 1 || textField.tag == 2) {
if (text.length == 4) {
textField.text = text;
[yourNewTextField becomeFirstResponder];
return NO;
}
}
// ...
return YES;
}
try resignFirstResponder and becomeFirstResponder when text length is 4
As pointed out in a comment, the reason you're only seeing 3 characters being printed out in your NSLog statement is because the changes haven't been applied to textField.text until the method returns YES. In order to have the application automatically select the next text field when the desired length is reached, you just need to call becomeFirstResponder on the next text field. For instance:
if(textLength == 4)
{
NSLog(#"doneeee");
// Here's how you can output the field's text, assuming you will return YES
NSLog(#"testfield: %#", [textField.text stringByReplacingCharactersInRange:range withString:string];
// Here's how you make the next field active
[nextTextFieldOutlet becomeFirstResponder]; // Or whatever you field is called.
}
return YES;
As a side note, you may want to put in some logic to return NO from this method if the field's length is greater than your desired length for the particular field. For instance, if you only want a max of 6 for your postal code field, check if the fieldLength > 6 and return NO in that case. That way, if someone tries to paste in a long string, it will reject it.

How to detect delete key on an UITextField in iOS 8?

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

iOS UItextfield reload after key hit

I am making a simple application that has a TextField and a TextView
Whatever we write in textfield will get updated in textview (Both are in a single View, and it is the Root view).
the textfield will get updated every time i hit any key in the keyboard.
I know that the delegate method "textfielddidEditing" helps , but it only helps when we click on the field or click back. I want the method that invokes every time when i hit anything in the keyboard.
Any help is appreciated.
I'm still learning iOS
Please use below textfield delegate method. Its easy and simple to use with all appen and delete mechanism. You can delete multiple characters too by selection. Just store newString value to your textView
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSMutableString *newString = [NSMutableString stringWithString:textField.text];
if (range.length > 0) {
[newString replaceCharactersInRange:range withString:string];
}
else {
[newString appendString:string];
}
NSLog(#"%#",newString);
return YES;
}
You can use UITextField Delegate method. It invokes every time when you hit anything in the keyboard. Write your code as per your requirement..
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.text.length>0)
{
self.yourtextview.text=textField.text;
}
return YES;
}
Hope it helps you..
The above code mentioned by Vidhyanad900 will work. But its if would fail in case a user deletes the text. (Hits backspace or selects complete text and deletes it)
Hence Modifying the code
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
int iNewLength = [textField length];
if([text isEqualToString:#""]) //If text is deleted
{
if([textField selectedTextRange]) //If Text is Seleceted And Deleted
iNewLength = [textField length] - [textField selectedRange].length;
else
iNewLength = [textField length] - 1;
}
if (iNewLength > 0)
{
self.yourtextview.text=textField.text;
}
return YES;
}

Set max length of UITextField in a UIAlertView

I want to set a max length to my UITexField but this is inserted in a UIAlertView,
I'm trying to find out the right information to solve this problem,on this beautiful site but there is nothing.
Could someone help me to this?
I think this is a better implementation of the UITextFieldDelegate method, as it will not remove the text end in front of the users eyes, it just prevents the user from typing in more text.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *newString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (newString.length > MAXLENGTH)
{
return NO;
}
return YES;
}
Don't forget to implement the UITextFieldDelegate protocol, and set your controller as the delegate of the text field (see Anoop Vaidya answer)
As you can access the alertView's textField by
UITextField *theTitleTextField = [alertView valueForKey:#"_bodyTextLabel"];
If you want to find your's textfield added, then use textFieldAtIndex: as suggested by Martin R.
Then implement UITextFieldDelegate protocol.
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if ([textField.text length] > MAXLENGTH) {
textField.text = [textField.text substringToIndex:MAXLENGTH-1];
return NO;
}
return YES;
}

Resources