How to resize UITextView when keyboard is shown with iOS 7

I have a view controller that contains a fullscreen UITextView . When the keyboard is displayed, I would like to resize the text view so that it is not hidden under the keyboard.

This is a fairly standard approach with iOS, as described in this question:

How to resize UITextView on iOS when keyboard appears?

However, with iOS 7, if a user enters a text view in the lower half of the screen, when the text is resized, the cursor remains off the screen. The text view only scrolls to display the cursor if you press when a user logs in.

+13
objective-c ios7
Sep 23 '13 at 20:57
source share
8 answers

While the answer given by @Divya leads me to the right solution (therefore I was rewarded with generosity), this is not a very clear answer! Here it is in detail:

The standard approach to ensuring that the text representation is not hidden by the on-screen keyboard is to update its frame when the keyboard is displayed, as described in detail in this question:

How to resize UITextView on iOS when keyboard appears?

However, with iOS 7, if you change the text view frame in your handler to notify UIKeyboardWillShowNotification , the cursor will remain off as described in this question.

The fix for this problem is to change the text frame instead in response to the delegate method textViewDidBeginEditing :

 @implementation ViewController { CGSize _keyboardSize; UITextView* textView; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. textView = [[UITextView alloc] initWithFrame:CGRectInset(self.view.bounds, 20.0, 20.0)]; textView.delegate = self; textView.returnKeyType = UIReturnKeyDone; textView.backgroundColor = [UIColor greenColor]; textView.textColor = [UIColor blackColor]; [self.view addSubview:textView]; NSMutableString *textString = [NSMutableString new]; for (int i=0; i<100; i++) { [textString appendString:@"cheese\rpizza\rchips\r"]; } textView.text = textString; } - (void)textViewDidBeginEditing:(UITextView *)textView1 { CGRect textViewFrame = CGRectInset(self.view.bounds, 20.0, 20.0); textViewFrame.size.height -= 216; textView.frame = textViewFrame; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ CGRect textViewFrame = CGRectInset(self.view.bounds, 20.0, 20.0); textView.frame = textViewFrame; [textView endEditing:YES]; [super touchesBegan:touches withEvent:event]; } @end 

NOTE. Unfortunately, textViewDidBeginEdting triggered before a UIKeyboardWillShowNotification notification, so you need to hardcode the height of the keyboard.

+3
Sep 27 '13 at 13:06 on
source share

I read the docs that talk about this topic . I transferred it to Swift, and it worked great for me.

This is used for a full UITextView page, such as iMessage.

I am using iOS 8.2 and Swift on Xcode 6.2, and here is my code. Just call setupKeyboardNotifications from your viewDidLoad or other initialization method.

 func setupKeyboardNotifications() { NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil) } func keyboardWasShown(aNotification:NSNotification) { let info = aNotification.userInfo let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as NSValue let kbSize = infoNSValue.CGRectValue().size let contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0) codeTextView.contentInset = contentInsets codeTextView.scrollIndicatorInsets = contentInsets } func keyboardWillBeHidden(aNotification:NSNotification) { let contentInsets = UIEdgeInsetsZero codeTextView.contentInset = contentInsets codeTextView.scrollIndicatorInsets = contentInsets } 

Also, if you have problems with the carriage being in the right place when it is rotated, check the orientation change and scroll to the desired position.

 override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) { scrollToCaretInTextView(codeTextView, animated: true) } func scrollToCaretInTextView(textView:UITextView, animated:Bool) { var rect = textView.caretRectForPosition(textView.selectedTextRange?.end) rect.size.height += textView.textContainerInset.bottom textView.scrollRectToVisible(rect, animated: animated) } 

Swift 3:

 func configureKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(aNotification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(aNotification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } func keyboardWasShown(aNotification:NSNotification) { let info = aNotification.userInfo let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as! NSValue let kbSize = infoNSValue.cgRectValue.size let contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0) textView.contentInset = contentInsets textView.scrollIndicatorInsets = contentInsets } func keyboardWillBeHidden(aNotification:NSNotification) { let contentInsets = UIEdgeInsets.zero textView.contentInset = contentInsets textView.scrollIndicatorInsets = contentInsets } 
+22
Mar 30 '15 at 12:47
source share

Using Auto Layout is much simpler (if you understand Auto Layout):

Instead of trying to identify and resize the affected views, you simply create a parent frame for the entire contents of your view. Then, if kbd appears, you will resize the frame, and if you set the limits correctly, the view will order all its children well. No need to bother with a lot of hard-to-read code for this.

In fact, in a similar question, I found a link to this excellent tutorial about this technique.

In addition, other examples here that use textViewDidBeginEditing instead of UIKeyboardWillShowNotification have one big problem:

If the user has an external Bluetooth keyboard, then the control will still be pressed, even if the on-screen keyboard does not appear. This is not good.

So, we summarize:

  • Use auto layout
  • Use the UIKeyboardWillShowNotification notification, not the TextEditField events, to decide when to view.

Alternatively, check LeoNatan's answer. This may be an even cleaner and simpler solution (I haven't tried it myself yet).

+4
Mar 17 '14 at 20:43
source share

Do not resize the text view. Instead, set the bottom of the contentInset and scrollIndicatorInsets to the height of the keyboard.

See my answer here: https://stackoverflow.com/a/312618/




Edit

The following changes have been made to your sample project:

 - (void)textViewDidBeginEditing:(UITextView *)textView { _caretVisibilityTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(_scrollCaretToVisible) userInfo:nil repeats:YES]; } - (void)_scrollCaretToVisible { //This is where the cursor is at. CGRect caretRect = [self.textView caretRectForPosition:self.textView.selectedTextRange.end]; if(CGRectEqualToRect(caretRect, _oldRect)) return; _oldRect = caretRect; //This is the visible rect of the textview. CGRect visibleRect = self.textView.bounds; visibleRect.size.height -= (self.textView.contentInset.top + self.textView.contentInset.bottom); visibleRect.origin.y = self.textView.contentOffset.y; //We will scroll only if the caret falls outside of the visible rect. if(!CGRectContainsRect(visibleRect, caretRect)) { CGPoint newOffset = self.textView.contentOffset; newOffset.y = MAX((caretRect.origin.y + caretRect.size.height) - visibleRect.size.height + 5, 0); [self.textView setContentOffset:newOffset animated:NO]; } } 

First, the setting of the old carriage position was deleted, and the animation was turned off. Now it seems to work well.

+3
Sep 23 '13 at 21:43
source share

The following works for me:

.h file

 @interface ViewController : UIViewController <UITextViewDelegate> { UITextView *textView ; } @property(nonatomic,strong)IBOutlet UITextView *textView; @end 

.m file

 @implementation ViewController @synthesize textView; - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 424.0f); //UITextView *textView = [[UITextView alloc] initWithFrame:textViewFrame]; textView.frame = textViewFrame; textView.delegate = self; textView.returnKeyType = UIReturnKeyDone; textView.backgroundColor = [UIColor greenColor]; textView.textColor = [UIColor blackColor]; [self.view addSubview:textView]; } - (BOOL)textViewShouldBeginEditing:(UITextView *)textView{ NSLog(@"textViewShouldBeginEditing:"); return YES; } - (void)textViewDidBeginEditing:(UITextView *)textView1 { NSLog(@"textViewDidBeginEditing:"); CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 224.0f); textView1.frame = textViewFrame; } - (BOOL)textViewShouldEndEditing:(UITextView *)textView{ NSLog(@"textViewShouldEndEditing:"); return YES; } - (void)textViewDidEndEditing:(UITextView *)textView{ NSLog(@"textViewDidEndEditing:"); } - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{ return YES; } - (void)textViewDidChange:(UITextView *)textView{ NSLog(@"textViewDidChange:"); } - (void)textViewDidChangeSelection:(UITextView *)textView{ NSLog(@"textViewDidChangeSelection:"); } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"touchesBegan:withEvent:"); CGRect textViewFrame = CGRectMake(20.0f, 20.0f, 280.0f, 424.0f); textView.frame = textViewFrame; [self.view endEditing:YES]; [super touchesBegan:touches withEvent:event]; } @end 
+2
Sep 26 '13 at 7:56
source share

I did this and his work completely.

  #define k_KEYBOARD_OFFSET 95.0 -(void)keyboardWillAppear { // Move current view up / down with Animation if (self.view.frame.origin.y >= 0) { [self moveViewUp:NO]; } else if (self.view.frame.origin.y < 0) { [self moveViewUp:YES]; } } -(void)keyboardWillDisappear { if (self.view.frame.origin.y >= 0) { [self moveViewUp:YES]; } else if (self.view.frame.origin.y < 0) { [self moveViewUp:NO]; } } -(void)textFieldDidBeginEditing:(UITextField *)sender { //if ([sender isEqual:_txtPassword]) // { //move the main view up, so the keyboard will not hide it. if (self.view.frame.origin.y >= 0) { [self moveViewUp:YES]; } //} } //Custom method to move the view up/down whenever the keyboard is appeared / disappeared -(void)moveViewUp:(BOOL)bMovedUp { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.4]; // to slide the view up CGRect rect = self.view.frame; if (bMovedUp) { // 1. move the origin of view up so that the text field will come above the keyboard rect.origin.y -= k_KEYBOARD_OFFSET; // 2. increase the height of the view to cover up the area behind the keyboard rect.size.height += k_KEYBOARD_OFFSET; } else { // revert to normal state of the view. rect.origin.y += k_KEYBOARD_OFFSET; rect.size.height -= k_KEYBOARD_OFFSET; } self.view.frame = rect; [UIView commitAnimations]; } - (void)viewWillAppear:(BOOL)animated { // register keyboard notifications to appear / disappear the keyboard [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillAppear) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillDisappear) name:UIKeyboardWillHideNotification object:nil]; } - (void)viewWillDisappear:(BOOL)animated { // unregister for keyboard notifications while moving to the other screen. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } 
0
Jun 09 '15 at 11:11
source share

This is my decision, July 2015, using Swift 1.2 on Xcode 6.4 focused on iOS 7.1 - a combination of several approaches. Loaned Johnston keyboard conveying Swift code. Its a little hack, but its simple and it works.

I have a UITextView of vanilla in one view.

I did not want to embed it in UIScrollView according to Apple documentation . I just wanted the UITextView to change when the software keyboard appeared, and change to the original when the keyboard was fired.

These are the basic steps:

  • Set up keyboard notifications
  • Set the layout constraint in "Interface Builder" (TextView to the bottom edge in my case)
  • Create an IBOutlet for this restriction in the corresponding code file so that you can programmatically configure it
  • Use keyboard notifications to capture events and get keyboard size.
  • Programmatically configure an IBOutlet restriction using keyboard size to resize a TextView.
  • Put everything back when the keyboard deviates.

So, to the code.

I set the restriction output at the top of the code file using simple drag and drop in the interface constructor: @IBOutlet weak var myUITextViewBottomConstraint: NSLayoutConstraint!

I also created a global variable in which I can reinforce the state of affairs before the keyboard comes out: var myUITextViewBottomConstraintBackup: CGFloat = 0

Make keyboard notifications, call this function in viewDidLoad or in any other start / install section:

 func setupKeyboardNotifications() { NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWasShown:"), name: UIKeyboardDidShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillBeHidden:"), name: UIKeyboardWillHideNotification, object: nil) } 

Then these two functions will be called automatically when the keyboard is displayed / rejected:

 func keyboardWasShown(aNotification:NSNotification) { let info = aNotification.userInfo let infoNSValue = info![UIKeyboardFrameBeginUserInfoKey] as! NSValue let kbSize = infoNSValue.CGRectValue().size let newHeight = kbSize.height //backup old constraint size myUITextViewBottomConstraintOld = myUITextViewBottomConstraint.constant // I subtract 50 because otherwise it leaves a gap between keyboard and text view. I'm sure this could be improved on. myUITextViewBottomConstraint.constant = newHeight - 50 func keyboardWillBeHidden(aNotification:NSNotification) { //restore to whatever AutoLayout set it before you messed with it myUITextViewBottomConstraint.constant = myUITextViewBottomConstraintOld } 

The code works with a minor problem:

  • It does not respond to the predictive text tape above the keyboard, opening / closing. That is, it will take into account its state when the keyboard is called, but if you must move it up or down while the keyboard is displayed, the restriction will not be changed. This is a separate event that needs to be processed. Not enough functionality for me to worry about.
0
Jul 12 '15 at 13:32
source share

@ Johnston found a good solution. Here the variant is changed using UIKeyboardWillChangeFrameNotification , which correctly takes into account keyboard size changes (i.e., shows / hides QuickType panel). It also correctly handles the case where the text view is embedded in the navigation controller (i.e., where the contentInset not zero). It is also written in Swift 2.

 override func viewDidLoad() { : NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification, object: nil, queue: nil) { (notification) -> Void in guard let userInfo = notification.userInfo, let keyboardFrameEndValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return } let windowCoordinatesKeyboardFrameEnd = keyboardFrameEndValue.CGRectValue() // window coordinates let keyboardFrameEnd = self.view.convertRect(windowCoordinatesKeyboardFrameEnd, fromView: nil) // view coordinates var inset = self.textView.contentInset inset.bottom = CGRectGetMaxY(self.textView.frame) - CGRectGetMinY(keyboardFrameEnd) // bottom inset is the bottom of textView minus top of keyboard self.textView.contentInset = inset self.textView.scrollIndicatorInsets = inset } } 
0
Oct 08 '15 at 23:10
source share



All Articles