How to programmatically move a UIScrollView to focus in keyboard control?

I have 6 UITextFields on my UIScrollView . Now I can scroll by user request. But when the keyboard appears, some text fields are hidden.

It is not comfortable.

How to scroll the view programmatically so that I make sure that the keyboard does not hide the text box?

+49
user-interface objective-c iphone
Jan 27 '09 at 19:27
source share
13 answers

Finally, a simple fix:

 UIScrollView* v = (UIScrollView*) self.view ; CGRect rc = [textField bounds]; rc = [textField convertRect:rc toView:v]; rc.origin.x = 0 ; rc.origin.y -= 60 ; rc.size.height = 400; [self.scroll scrollRectToVisible:rc animated:YES]; 

Now I think that this is only a combination with the link above and is installed!

+38
03 Feb '09 at 1:26
source share

Here is what worked for me. The presence of an instance variable that contains the offset value of the UIScrollView before the view is configured for the keyboard, so that you can restore the previous state after returning the UITextField:

 //header @interface TheViewController : UIViewController <UITextFieldDelegate> { CGPoint svos; } //implementation - (void)textFieldDidBeginEditing:(UITextField *)textField { svos = scrollView.contentOffset; CGPoint pt; CGRect rc = [textField bounds]; rc = [textField convertRect:rc toView:scrollView]; pt = rc.origin; pt.x = 0; pt.y -= 60; [scrollView setContentOffset:pt animated:YES]; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [scrollView setContentOffset:svos animated:YES]; [textField resignFirstResponder]; return YES; } 
+77
Mar 18
source share

I have put together a universal subclass embedded in UIScrollView and UITableView that takes care of moving all text fields in it from the keyboard.

When the keyboard is about to appear, the subclass will find a preview to be edited and adjust its frame and content offset to make sure that the view is visible, with an animation corresponding to the keyboard popup. When the keyboard disappears, it restores the previous size.

It should work with any setting, either with a UITableView-based interface or with one of the manually placed views.

Here it is.




 (For google: TPKeyboardAvoiding, TPKeyboardAvoidingScrollView, TPKeyboardAvoidingCollectionView.) Editor note: TPKeyboardAvoiding seems to be continually updated and fresh, as of 2014. 
+22
Apr 12 2018-11-11T00:
source share

If you set the delegate your text fields to a controller object in your program, you can implement this object with the textFieldDidBeginEditing: and textFieldShouldReturn: methods. The first method can then be used to scroll to the text field, and the second method can be used to scroll back.

You can find the code I used for this on my blog: Slide UITextView around to avoid the keyboard . I have not tested this code for textual representations in UIScrollView , but it should work.

+2
Jan 27 '09 at 22:31
source share

simple and best

 - (void)textFieldDidBeginEditing:(UITextField *)textField { // self.scrlViewUI.contentOffset = CGPointMake(0, textField.frame.origin.y); [_scrlViewUI setContentOffset:CGPointMake(0,textField.center.y-90) animated:YES]; tes=YES; [self viewDidLayoutSubviews]; } 
+2
Jun 23 '14 at 10:11
source share

The answers posted so far have not worked for me, since I have a pretty deep nested UIViews structure. Also, I had a problem in that some of these answers only work with specific device orientations.

Here is my solution, which I hope will make you spend a little time on this.

My UIViewTextView comes from UIView, is a UITextView delegate, and adds a UITextView after reading some parameters from the XML file for that UITextView (this part of XML is missing here for clarity).

Here is the definition of the private interface:

 #import "UIViewTextView.h" #import <CoreGraphics/CoreGraphics.h> #import <CoreGraphics/CGColor.h> @interface UIViewTextView (/**/) { @private UITextView *tf; /* * Current content scroll view * position and frame */ CGFloat currentScrollViewPosition; CGFloat currentScrollViewHeight; CGFloat kbHeight; CGFloat kbTop; /* * contentScrollView is the UIScrollView * that contains ourselves. */ UIScrollView contentScrollView; } @end 

In the init method, I need to register event handlers:

 @implementation UIViewTextView - (id) initWithScrollView:(UIScrollView*)scrollView { self = [super init]; if (self) { contentScrollView = scrollView; // ... tf = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 241, 31)]; // ... configure tf and fetch data for it ... tf.delegate = self; // ... NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(keyboardWasShown:) name: UIKeyboardWillShowNotification object:nil]; [nc addObserver:self selector:@selector(keyboardWasHidden:) name: UIKeyboardWillHideNotification object:nil]; [self addSubview:tf]; } return(self); } 

Once this is done, we need to handle the show event. It is called before calling textViewBeginEditing, so we can use it to find out some properties of the keyboard. Essentially, we want to know the height of the keyboard. This, unfortunately, should be taken from its width property in landscape mode:

 -(void)keyboardWasShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]; CGSize kbSize = kbRect.size; CGRect screenRect = [[UIScreen mainScreen] bounds]; CGFloat sWidth = screenRect.size.width; CGFloat sHeight = screenRect.size.height; UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; if ((orientation == UIDeviceOrientationPortrait) ||(orientation == UIDeviceOrientationPortraitUpsideDown)) { kbHeight = kbSize.height; kbTop = sHeight - kbHeight; } else { //Note that the keyboard size is not oriented //so use width property instead kbHeight = kbSize.width; kbTop = sWidth - kbHeight; } 

Next, we need to scroll through the page when we start editing. We do it here:

 - (void) textViewDidBeginEditing:(UITextView *)textView { /* * Memorize the current scroll position */ currentScrollViewPosition = contentScrollView.contentOffset.y; /* * Memorize the current scroll view height */ currentScrollViewHeight = contentScrollView.frame.size.height; // My top position CGFloat myTop = [self convertPoint:self.bounds.origin toView:[UIApplication sharedApplication].keyWindow.rootViewController.view].y; // My height CGFloat myHeight = self.frame.size.height; // My bottom CGFloat myBottom = myTop + myHeight; // Eventual overlap CGFloat overlap = myBottom - kbTop; /* * If there no overlap, there nothing to do. */ if (overlap < 0) { return; } /* * Calculate the new height */ CGRect crect = contentScrollView.frame; CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight + overlap); /* * Set the new height */ [contentScrollView setFrame:nrect]; /* * Set the new scroll position */ CGPoint npos; npos.x = contentScrollView.contentOffset.x; npos.y = contentScrollView.contentOffset.y + overlap; [contentScrollView setContentOffset:npos animated:NO]; } 

When we finish editing, we do this with the reset scroll position:

 - (void) textViewDidEndEditing:(UITextView *)textView { /* * Reset the scroll view position */ CGRect crect = contentScrollView.frame; CGRect nrect = CGRectMake(crect.origin.x, crect.origin.y, crect.size.width, currentScrollViewHeight); [contentScrollView setFrame:nrect]; /* * Reset the scroll view height */ CGPoint npos; npos.x = contentScrollView.contentOffset.x; npos.y = currentScrollViewPosition; [contentScrollView setContentOffset:npos animated:YES]; [tf resignFirstResponder]; // ... do something with your data ... } 

There is no hidden event handler left in the keyboard; we leave it anyway:

 -(void)keyboardWasHidden:(NSNotification*)aNotification { } 

What is it.

 /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ @end 
+1
Aug 21 2018-12-12T00:
source share

I know that this is old, but still none of the above solutions possessed all the necessary positioning things necessary for this "ideal" without errors, backward compatibility and without flickering.
Let me share my solution (assuming you configured UIKeyboardWill(Show|Hide)Notification ):

 // Called when UIKeyboardWillShowNotification is sent - (void)keyboardWillShow:(NSNotification*)notification { // if we have no view or are not visible in any window, we don't care if (!self.isViewLoaded || !self.view.window) { return; } NSDictionary *userInfo = [notification userInfo]; CGRect keyboardFrameInWindow; [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow]; // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil]; CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView); UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0); // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary. [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; _scrollView.contentInset = newContentInsets; _scrollView.scrollIndicatorInsets = newContentInsets; /* * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element * that should be visible, eg a purchase button below an amount text field * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields */ if (_focusedControl) { CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view. CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y; CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height; // this is the visible part of the scroll view that is not hidden by the keyboard CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height; if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question // scroll up until the control is in place CGPoint newContentOffset = _scrollView.contentOffset; newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight); // make sure we don't set an impossible offset caused by the "nice visual offset" // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight); [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) { // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field CGPoint newContentOffset = _scrollView.contentOffset; newContentOffset.y = controlFrameInScrollView.origin.y; [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code } } [UIView commitAnimations]; } // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillHide:(NSNotification*)notification { // if we have no view or are not visible in any window, we don't care if (!self.isViewLoaded || !self.view.window) { return; } NSDictionary *userInfo = notification.userInfo; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; // undo all that keyboardWillShow-magic // the scroll view will adjust its contentOffset apropriately _scrollView.contentInset = UIEdgeInsetsZero; _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero; [UIView commitAnimations]; } 
+1
Mar 01 '13 at 17:17
source share

You can check it out: https://github.com/michaeltyson/TPKeyboardAvoiding (I used this sample for my applications). It works so well. Hope this helps you.




In fact, here is a complete tutorial on using TPKeyboardAvoiding that can help someone

(1) download the zip file from the github link. add these four files to your Xcode project:

enter image description here

(2) build your beautiful shape in IB. add a UIScrollView. place form elements INSIDE VIEWING . (Note: extremely useful tip regarding the interface constructor: https://stackoverflow.com/a/166779/ )

enter image description here

(3) click in the scroll view . then in the upper right, third button you will see the word "UIScrollView". using copy and paste, change it to "TPKeyboardAvoidingScrollView"

enter image description here

(4) that he is. put the application in the application store and pay for your client.

(Alternatively, just click the Inspector tab in the scroll list. You might want to enable or disable bouncing, and the scroll bars are your preference.)

Personal comment. I highly recommend using scroll view (or collection view) for input forms in almost all cases. Do not use table view. This is a problem for many reasons. and pretty simple, incredibly easy to use scrolling. just lay it out as you want. this is 100% wysiwyg in the interface builder. hope this helps

+1
Mar 31 '14 at 16:00
source share

This is my code, I hope it helps you. It works fine if you have a lot of text field.

 CGPoint contentOffset; bool isScroll; - (void)textFieldDidBeginEditing:(UITextField *)textField { contentOffset = self.myScroll.contentOffset; CGPoint newOffset; newOffset.x = contentOffset.x; newOffset.y = contentOffset.y; //check push return in keyboar if(!isScroll){ //180 is height of keyboar newOffset.y += 180; isScroll=YES; } [self.myScroll setContentOffset:newOffset animated:YES]; } - (BOOL)textFieldShouldReturn:(UITextField *)textField{ //reset offset of content isScroll = NO; [self.myScroll setContentOffset:contentOffset animated:YES]; [textField endEditing:true]; return true; } 

we have a contentOffset point to save the contentoffset scrollview before showing the keyboar. Then we scroll through the contents for y about 180 (keychain height). when you touch return to the keychain, we scroll the contents to the old point (this is contentOffset). If you have a lot of text field, you do not touch the return to keyboar, but you touch another text field, it will be +180. So we have a touch check return

+1
Jan 29 '15 at 9:25
source share

I have modified some of the above solutions to make them easier to understand and use. I used IBOutlet so that several text fields could refer to the function with the “Editing started” from the “Sent events” text fields. ** Do not forget to open the outlet to view the scroll

 - (IBAction)moveViewUpwards:(id)sender { CGRect rc = [sender convertRect:[sender bounds] toView:scrollView]; rc.origin.x = 0 ; rc.origin.y -= 60 ; rc.size.height = 400; [scrollView scrollRectToVisible:rc animated:YES]; } 
0
Aug 24 '13 at 15:40
source share

Use any of them,

 CGPoint bottomOffset = CGPointMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height); [self.MainScrollView setContentOffset:bottomOffset animated:YES]; 

or

 [self.MainScrollView scrollRectToVisible:CGRectMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height-30, MainScrollView.frame.size.width, MainScrollView.frame.size.height) animated:YES]; 
0
Jan 21 '15 at 14:46
source share

I think it's better to use keyboard notifications because you don't know if the first responder (with a focus on control) is a text box or text box (or any other). So juste create a category to find the first responder:

 #import "UIResponder+FirstResponder.h" static __weak id currentFirstResponder; @implementation UIResponder (FirstResponder) +(id)currentFirstResponder { currentFirstResponder = nil; [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil]; return currentFirstResponder; } -(void)findFirstResponder:(id)sender { currentFirstResponder = self; } @end 

then

 -(void)keyboardWillShowNotification:(NSNotification*)aNotification{ contentScrollView.delegate=nil; contentScrollView.scrollEnabled=NO; contentScrollViewOriginalOffset = contentScrollView.contentOffset; UIResponder *lc_firstResponder = [UIResponder currentFirstResponder]; if([lc_firstResponder isKindOfClass:[UIView class]]){ UIView *lc_view = (UIView *)lc_firstResponder; CGRect lc_frame = [lc_view convertRect:lc_view.bounds toView:contentScrollView]; CGPoint lc_point = CGPointMake(0, lc_frame.origin.y-lc_frame.size.height); [contentScrollView setContentOffset:lc_point animated:YES]; } } 

Finally, turn off scrolling and set the delegate to zero, then restore it to avoid some actions during the release of the first responder. Like james_womack, keep the original offset to restore it in the keyboardWillHideNotification method.

 -(void)keyboardWillHideNotification:(NSNotification*)aNotification{ contentScrollView.delegate=self; contentScrollView.scrollEnabled=YES; [contentScrollView setContentOffset:contentScrollViewOriginalOffset animated:YES]; } 
0
Aug 31 '15 at 7:35
source share

In Swift 1.2+, do something like this:

 class YourViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() _yourTextField.delegate = self //make sure you have the delegate set to this view controller for each of your textFields so textFieldDidBeginEditing can be called for each one ... } func textFieldDidBeginEditing(textField: UITextField) { var point = textField.convertPoint(textField.frame.origin, toView: _yourScrollView) point.x = 0.0 //if your textField does not have an origin at 0 for x and you don't want your scrollView to shift left and right but rather just up and down _yourScrollView.setContentOffset(point, animated: true) } func textFieldDidEndEditing(textField: UITextField) { //Reset scrollview once done editing scrollView.setContentOffset(CGPoint.zero, animated: true) } } 
0
Mar 09 '16 at 22:59
source share



All Articles