-
Notifications
You must be signed in to change notification settings - Fork 1
AA 1.19. Accepting User Text Input with UITextField
A text field allows only a single line of text to be input/displayed. As a result, the default height of a text field is only 31 points. In Interface Builder, this height cannot be modified, but if you are creating your text field in code, you can change the text field’s height. A change in height, though, will not change the number of lines you can render in a text field, which is always 1.
Let’s start with the implementation file of our view controller to define our text field:
@interface ViewController () <UITextFieldDelegate> @property (nonatomic, strong) UITextField *myTextField; @property (nonatomic, strong) UILabel *labelCounter; @property (nonatomic, strong) UILabel *currencyLabel; @end
And then let’s create the text field:
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CGRect textFieldFrame = CGRectMake(0.0f, 0.0f, 200.0f, 20.0f); self.myTextField = [[UITextField alloc] initWithFrame:textFieldFrame]; self.myTextField.delegate = self; self.myTextField.borderStyle = UITextBorderStyleRoundedRect; self.myTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; self.myTextField.textAlignment = NSTextAlignmentCenter; self.myTextField.text = @"Sir Richard Branson"; self.myTextField.center = self.view.center; [self.view addSubview:self.myTextField]; }
In order to create this text field, we used various properties of UITextField.
borderStyle This property is of type UITextBorderStyle and specifies how the text field should render its borders.
contentVerticalAlignment This value is of type UIControlContentVerticalAlignment and tells the text field how the text should appear, vertically, in the boundaries of the control. If we didn’t center the text vertically, it would appear on the top-left corner of the text field by default.
textAlignment This property is of type NSTextAlignment and specifies the horizontal alignment of the text in a text field. In this example, we have centered the text horizontally.
text This is a read/write property: you can both read from it and write to it. Reading from it will return the text field’s current text, and writing to it will set the text field’s text to the value that you specify.
A text field sends delegate messages to its delegate object. These messages get sent, for instance, when the user starts editing the text inside a text field, when the user enters any character into the text field (changing its contents in any way), and when the user finishes editing the field (by leaving the field). To get notified of these events, set the delegate property of the text field to your object. The delegate of a text field must conform to the UITextFieldDelegate protocol, so let’s first take care of this:
@interface ViewController () <UITextFieldDelegate>
Hold down the Command key on your computer and click the UITextFieldDelegate protocol in Xcode. You will see all the methods that this protocol gives you control over. Here are those methods with descriptions of when they get called:
textFieldShouldBeginEditing: A method that returns a BOOL telling the text field (the parameter to this method) whether it should start getting edited by the user or not. Return NO if you don’t want the user to edit your text field. This method gets fired as soon as the user taps on the text field with the goal of editing its content (assuming the text field allows editing).
textFieldDidBeginEditing: Gets called when the text field starts to get edited by the user. This method gets called when the user has already tapped on the text field and the textFieldShould BeginEditing: delegate method of the text field returned YES, telling the text field it is OK for the user to edit the content of the text field.
textFieldShouldEndEditing: Returns a BOOL telling the text field whether it should end its current editing session or not. This method gets called when the user is about to leave the text field or the first responder is switching to another data entry field. If you return NO from this method, the user will not be able to switch to another text entry field, and the keyboard will stay on the screen. textFieldDidEndEditing: Gets called when the editing session of the text field ends. This happens when the user decides to edit some other data entry field or uses a button provided by the supplier of the app to dismiss the keyboard
shown for the text field. textField:shouldChangeCharactersInRange:replacementString: Gets called whenever the text inside the text field is modified. The return value of this method is a Boolean. If you return YES, you say that you allow the text to be changed. If you return NO, the change in the text of the text field will not be confirmed and will not happen.
textFieldShouldClear: Each text field has a clear button that is usually a circular X button. When the user presses this button, the contents of the text field will automatically get erased. We need to manually enable the clear button, though. If you have enabled the clear button and you return NO to this method, that gives the user the impression that your app isn’t working, so make sure you know what you are doing. It is a very poor user experience if the user sees a clear button and presses it but doesn’t see the text in the text field get erased.
textFieldShouldReturn: Gets called when the user has pressed the Return/Enter key on the keyboard, trying to dismiss the keyboard. You should assign the text field as the first responder in this method.
Now for the creation of the text field along with the label and the text field delegate methods we require. We skip implementing many of the UITextFieldDelegate meth‐ ods, because we don’t need all of them in this example:
- (void) calculateAndDisplayTextFieldLengthWithText:(NSString *)paramText{ NSString *characterOrCharacters = @"Characters"; if ([paramText length] == 1){ characterOrCharacters = @"Character"; } self.labelCounter.text = [NSString stringWithFormat:@"%lu %@", (unsigned long)[paramText length], characterOrCharacters]; } - (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ if ([textField isEqual:self.myTextField]){ NSString *wholeText = [textField.text stringByReplacingCharactersInRange:range withString:string]; [self calculateAndDisplayTextFieldLengthWithText:wholeText]; } return YES; } - (BOOL)textFieldShouldReturn:(UITextField *)textField{ [textField resignFirstResponder]; return YES; } - (void)viewDidLoad { [super viewDidLoad]; CGRect textFieldFrame = CGRectMake(0.0f, 0.0f, 200.0f, 20.0f); self.myTextField = [[UITextField alloc] initWithFrame:textFieldFrame]; self.myTextField.delegate = self; self.myTextField.borderStyle = UITextBorderStyleRoundedRect; self.myTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; self.myTextField.textAlignment = NSTextAlignmentCenter; self.myTextField.text = @"Sir Richard Branson"; self.myTextField.center = self.view.center; self.myTextField.clearButtonMode = YES; [self.view addSubview:self.myTextField]; self.myTextField.placeholder = @"Enter text here..."; CGRect labelCounterFrame = self.myTextField.frame; labelCounterFrame.origin.y += textFieldFrame.size.height - 100; self.labelCounter = [[UILabel alloc] initWithFrame:labelCounterFrame]; [self.view addSubview:self.labelCounter]; [self calculateAndDisplayTextFieldLengthWithText:self.myTextField.text]; }
One important calculation we are doing is in the textField:shouldChangeCharactersInRange:replacementString: method. There, we declare and use a variable called wholeText. When this method gets called, the replacementString parameter specifies the string that the user has entered into the text field. You might be thinking that the user can enter only one character at a time, so why can’t this field be a char? But don’t forget that the user can paste a whole chunk of text into a text field, so this parameter needs to be a string. The shouldChangeCharactersInRange parameter specifies where, in terms of location inside the text field’s text, the user is entering the text. So using these two parameters, we will create a string that first reads the whole text inside the text field and then uses the given range to place the new text inside the old text. With this, we will come up with the text that will appear in the text field after the textField:shouldChan geCharactersInRange:replacementString: method returns YES.
In addition to displaying text, a text field can also display a placeholder. A placeholder is the text displayed before the user has entered any text in the text field, while the text field’s text property is empty. This can be any string that you wish, and setting it will help give the user an indication as to what this text field is for. Many use this placeholder to tell the user what type of value she can enter in that text field. For instance, in Figure 1-49, the two text fields (password and description) have placeholders that say “Required,” etc. You can use the placeholder property of the text field to set or get the current placeholder. Here is an example:
//currencyLabel.text = [[[NSNumberFormatter alloc] init] currencySymbol]; self.currencyLabel.text = @"... "; self.currencyLabel.font = self.myTextField.font; [self.currencyLabel sizeToFit]; self.myTextField.rightView = self.currencyLabel; self.myTextField.rightViewMode = UITextFieldViewModeAlways; self.currencyLabel.userInteractionEnabled = YES; UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)]; [self.currencyLabel addGestureRecognizer:recognizer]; - (void)tapAction { //NSLog(@"tap performed"); UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"You've been delivered an alert" delegate:nil cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ok", @"Other button", nil]; [alert show]; }
If we simply assign a view to the leftView or to the rightView properties of a text field, those views will not appear automatically by default. When they show up on the screen depends on the mode that governs their appearance, and you can control that mode using the leftViewMode and rightViewMode properties, respectively. These modes are of type UITextFieldViewMode:
typedef NS_ENUM(NSInteger, UITextFieldViewMode) { UITextFieldViewModeNever, UITextFieldViewModeWhileEditing, UITextFieldViewModeUnlessEditing, UITextFieldViewModeAlways }; - (void)textViewDidChange:(UITextView *)textView { CGRect line = [textView caretRectForPosition: textView.selectedTextRange.start]; CGFloat overflow = line.origin.y + line.size.height - ( textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top ); if ( overflow > 0 ) { // We are at the bottom of the visible text and introduced a line feed, scroll down (iOS 7 does not do it) // Scroll caret to visible area CGPoint offset = textView.contentOffset; offset.y += overflow + 7; // leave 7 pixels margin // Cannot animate with setContentOffset:animated: or caret will not appear [UIView animateWithDuration:.2 animations:^{ [textView setContentOffset:offset]; }]; } }
stackoverflow.com/questions/18966675/uitextview-in-ios7-clips-the-last-line-of-text-string
-(void)textViewDidChange:(UITextView *)textView { CGRect line = [textView caretRectForPosition: textView.selectedTextRange.start]; CGFloat overflow = line.origin.y + line.size.height - ( textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top ); if ( overflow > 0 ) { // We are at the bottom of the visible text and introduced a line feed, scroll down (iOS 7 does not do it) // Scroll caret to visible area CGPoint offset = textView.contentOffset; offset.y += overflow + 7; // leave 7 pixels margin // Cannot animate with setContentOffset:animated: or caret will not appear [UIView animateWithDuration:.2 animations:^{ [textView setContentOffset:offset]; }]; } }