Keyboard for UITextView in iOS covers text I'm editing [duplicate] - ios

With the iOS SDK:
I have a UIView with UITextFields that bring up a keyboard. I need it to be able to:
Allow scrolling of the contents of the UIScrollView to see the other text fields once the keyboard is brought up
Automatically "jump" (by scrolling up) or shortening
I know that I need a UIScrollView. I've tried changing the class of my UIView to a UIScrollView, but I'm still unable to scroll the textboxes up or down.
Do I need both a UIView and a UIScrollView? Does one go inside the other?
What needs to be implemented in order to automatically scroll to the active text field?
Ideally as much of the setup of the components as possible will be done in Interface Builder. I'd like to only write code for what needs it.
Note: the UIView (or UIScrollView) that I'm working with is brought up by a tabbar (UITabBar), which needs to function as normal.
I am adding the scroll bar just for when the keyboard comes up. Even though it's not needed, I feel like it provides a better interface because then the user can scroll and change textboxes, for example.
I've got it working where I change the frame size of the UIScrollView when the keyboard goes up and down. I'm simply using:
-(void)textFieldDidBeginEditing:(UITextField *)textField {
//Keyboard becomes visible
scrollView.frame = CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50); // Resize
}
-(void)textFieldDidEndEditing:(UITextField *)textField {
// Keyboard will hide
scrollView.frame = CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height + 215 - 50); // Resize
}
However, this doesn't automatically "move up" or center the lower text fields in the visible area, which is what I would really like.

You will only need a ScrollView if the contents you have now do not fit in the iPhone screen. (If you are adding the ScrollView as the superview of the components just to make the TextField scroll up when keyboard comes up, then it's not needed.)
The standard way to prevent the TextFields from being covered by the keyboard is to move the view up/down whenever the keyboard is shown.
Here is some sample code:
#define kOFFSET_FOR_KEYBOARD 80.0
-(void)keyboardWillShow {
// Animate the current view out of the way
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)keyboardWillHide {
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if ([sender isEqual:mailTf])
{
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
}
}
//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3]; // if you want to slide up the view
CGRect rect = self.view.frame;
if (movedUp)
{
// 1. move the view's origin up so that the text field that will be hidden come above the keyboard
// 2. increase the size of the view so that the area behind the keyboard is covered up.
rect.origin.y -= kOFFSET_FOR_KEYBOARD;
rect.size.height += kOFFSET_FOR_KEYBOARD;
}
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}

I was also having a lot of issue with a UIScrollView composing of multiple UITextFields, of which, one or more of them would get obscured by the keyboard when they are being edited.
Here are some things to consider if your UIScrollView is not properly scrolling.
1) Ensure that your contentSize is greater than the UIScrollView frame size. The way to understand UIScrollViews is that the UIScrollView is like a viewing window on the content defined in the contentSize. So when in order for the UIScrollview to scroll anywhere, the contentSize must be greater than the UIScrollView. Else, there is no scrolling required as everything defined in the contentSize is already visible. BTW, default contentSize = CGSizeZero.
2) Now that you understand that the UIScrollView is really a window into your "content", the way to ensure that the keyboard is not obscuring your UIScrollView's viewing "window" would be to resize the UIScrollView so that when the keyboard is present, you have the UIScrollView window sized to just the original UIScrollView frame.size.height minus the height of the keyboard. This will ensure that your window is only that small viewable area.
3) Here's the catch: When I first implemented this I figured I would have to get the CGRect of the edited textfield and call UIScrollView's scrollRecToVisible method. I implemented the UITextFieldDelegate method textFieldDidBeginEditing with the call to the scrollRecToVisible method. This actually worked with a strange side effect that the scrolling would snap the UITextField into position. For the longest time I couldn't figure out what it was. Then I commented out the textFieldDidBeginEditing Delegate method and it all work!!(???). As it turned out, I believe the UIScrollView actually implicitly brings the currently edited UITextField into the viewable window implicitly. My implementation of the UITextFieldDelegate method and subsequent call to the scrollRecToVisible was redundant and was the cause of the strange side effect.
So here are the steps to properly scroll your UITextField in a UIScrollView into place when the keyboard appears.
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.view.window];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.view.window];
keyboardIsShown = NO;
//make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
CGSize scrollContentSize = CGSizeMake(320, 345);
self.scrollView.contentSize = scrollContentSize;
}
- (void)keyboardWillHide:(NSNotification *)n
{
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// resize the scrollview
CGRect viewFrame = self.scrollView.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
viewFrame.size.height += (keyboardSize.height - kTabBarHeight);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.scrollView setFrame:viewFrame];
[UIView commitAnimations];
keyboardIsShown = NO;
}
- (void)keyboardWillShow:(NSNotification *)n
{
// This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown. This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`. If we were to resize the `UIScrollView` again, it would be disastrous. NOTE: The keyboard notification will fire even when the keyboard is already shown.
if (keyboardIsShown) {
return;
}
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// resize the noteView
CGRect viewFrame = self.scrollView.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.scrollView setFrame:viewFrame];
[UIView commitAnimations];
keyboardIsShown = YES;
}
Register for the keyboard notifications at viewDidLoad
Unregister for the keyboard nofitications at viewDidUnload
Ensure that the contentSize is set and greater than your UIScrollView at viewDidLoad
Shrink the UIScrollView when the keyboard is present
Revert back the UIScrollView when the keyboard goes away.
Use an ivar to detect if the keyboard is already shown on the screen since the keyboard notifications are sent each time a UITextField is tabbed even if the keyboard is already present to avoid shrinking the UIScrollView when it's already shrunk
One thing to note is that the UIKeyboardWillShowNotification will fire even when the keyboard is already on the screen when you tab on another UITextField. I took care of this by using an ivar to avoid resizing the UIScrollView when the keyboard is already on the screen. Inadvertently resizing the UIScrollView when the keyboard is already there would be disastrous!
Hope this code saves some of you a lot of headache.

It's actually best just to use Apple's implementation, as provided in the docs. However, the code they provide is faulty. Replace the portion found in keyboardWasShown: just below the comments to the following:
NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);
CGPoint origin = activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
[backScrollView setContentOffset:scrollPoint animated:YES];
}
The problems with Apple's code are these:
(1) They always calculate if the point is within the view's frame, but it's a ScrollView, so it may already have scrolled and you need to account for that offset:
origin.y -= scrollView.contentOffset.y
(2) They shift the contentOffset by the height of the keyboard, but we want the opposite (we want to shift the contentOffset by the height that is visible on the screen, not what isn't):
activeField.frame.origin.y-(aRect.size.height)

In textFieldDidBeginEditting and in textFieldDidEndEditing call the function [self animateTextField:textField up:YES] like so:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField:textField up:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField:textField up:NO];
}
-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
const int movementDistance = -130; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? movementDistance : -movementDistance);
[UIView beginAnimations: #"animateTextField" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
I hope this code will help you.
Swift 5
func animateTextField(textField: UITextField, up: Bool) {
let movementDistance: CGFloat = -130
let movementDuration: Double = 0.3
var movement:CGFloat = 0
if up {
movement = movementDistance
} else {
movement = -movementDistance
}
UIView.animate(withDuration: movementDuration, delay: 0, options: [.beginFromCurrentState]) {
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
animateTextField(textField: textField, up: true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
animateTextField(textField: textField, up: false)
}

Just using TextFields:
1a) Using Interface Builder: Select All TextFields => Edit => Embed In => ScrollView
1b) Manually embed TextFields in UIScrollView called scrollView
2) Set UITextFieldDelegate
3) Set each textField.delegate = self; (or make connections in Interface Builder)
4) Copy / Paste:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
[scrollView setContentOffset:scrollPoint animated:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[scrollView setContentOffset:CGPointZero animated:YES];
}

For Universal Solution, Here was my approach for implementing IQKeyboardManager.
Step1:- I Added global notifications of UITextField, UITextView, and UIKeyboard in a singleton class. I call it IQKeyboardManager.
Step2:- If found UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotification or UITextViewTextDidBeginEditingNotification notifications, I try to get topMostViewController instance from the UIWindow.rootViewController hierarchy. In order to properly uncover UITextField/UITextView on it, topMostViewController.view's frame needs to be adjusted.
Step3:- I calculated expected move distance of topMostViewController.view with respect to first responded UITextField/UITextView.
Step4:- I moved topMostViewController.view.frame up/down according to the expected move distance.
Step5:- If found UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotification or UITextViewTextDidEndEditingNotification notification, I again try to get topMostViewController instance from the UIWindow.rootViewController hierarchy.
Step6:- I calculated disturbed distance of topMostViewController.view which needs to be restored to it's original position.
Step7:- I restored topMostViewController.view.frame down according to the disturbed distance.
Step8:- I instantiated singleton IQKeyboardManager class instance on app load, so every UITextField/UITextView in the app will adjust automatically according to the expected move distance.
That's all IQKeyboardManager do for you with NO LINE OF CODE really!! only need to drag and drop related source file to project. IQKeyboardManager also support Device Orientation, Automatic UIToolbar Management, KeybkeyboardDistanceFromTextField and much more than you think.

I've put together a universal, drop-in UIScrollView, UITableView and even UICollectionView subclass that takes care of moving all text fields within it out of the way of the keyboard.
When the keyboard is about to appear, the subclass will find the subview that's about to be edited, and adjust its frame and content offset to make sure that view is visible, with an animation to match the keyboard pop-up. When the keyboard disappears, it restores its prior size.
It should work with basically any setup, either a UITableView-based interface, or one consisting of views placed manually.
Here' tis: solution for moving text fields out of the way of the keyboard

For Swift Programmers :
This will do everything for you, just put these in your view controller class and implement the UITextFieldDelegate to your view controller & set the textField's delegate to self
textField.delegate = self // Setting delegate of your UITextField to self
Implement the delegate callback methods:
func textFieldDidBeginEditing(textField: UITextField) {
animateViewMoving(true, moveValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
animateViewMoving(false, moveValue: 100)
}
// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
let movementDuration:NSTimeInterval = 0.3
let movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations( "animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration )
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
UIView.commitAnimations()
}
For Swift 4, 4.2, 5:
Change
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
to
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
Last note about this implementation: If you push another view controller onto the stack while the keyboard is shown, this will create an error where the view is returned back to its center frame but keyboard offset is not reset. For example, your keyboard is the first responder for nameField, but then you push a button that pushes your Help View Controller onto your stack. To fix the offset error, make sure to call nameField.resignFirstResponder() before leaving the view controller, ensuring that the textFieldDidEndEditing delegate method is called as well. I do this in the viewWillDisappear method.

There are already a lot of answers, but still none of the solutions above had all the fancy positioning stuff required for a "perfect" bug-free, backwards compatible and flicker-free animation. (bug when animating frame/bounds and contentOffset together, different interface orientations, iPad split keyboard, ...)
Let me share my solution:
(assuming you have set up 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, e.g. 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];
}

Shiun said "As it turned out, I believe the UIScrollView actually implicitly brings the currently edited UITextField into the viewable window implicitly" This seems to be true for iOS 3.1.3, but not 3.2, 4.0, or 4.1. I had to add an explicit scrollRectToVisible in order to make the UITextField visible on iOS >= 3.2.

One thing to consider is whether you ever want to use a UITextField on its own. I haven’t come across any well-designed iPhone apps that actually use UITextFields outside of UITableViewCells.
It will be some extra work, but I recommend you implement all data entry views a table views. Add a UITextView to your UITableViewCells.

This document details a solution to this problem. Look at the source code under 'Moving Content That Is Located Under the Keyboard'. It's pretty straightforward.
EDIT: Noticed there's a wee glitch in the example. You will probably want to listen for UIKeyboardWillHideNotification instead of UIKeyboardDidHideNotification. Otherwise the scroll view behind of the keyboard will be clipped for the duration of the keyboard closing animation.

Easiest solution found
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}

A little fix that works for many UITextFields:
#pragma mark UIKeyboard handling
#define kMin 150
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if (currTextField) {
[currTextField release];
}
currTextField = [sender retain];
// Move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
[self setViewMovedUp:YES];
}
}
// Method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3]; // If you want to slide up the view
CGRect rect = self.view.frame;
if (movedUp)
{
// 1. move the view's origin up so that the text field that will be hidden come above the keyboard
// 2. increase the size of the view so that the area behind the keyboard is covered up.
rect.origin.y = kMin - currTextField.frame.origin.y ;
}
else
{
// Revert back to the normal state.
rect.origin.y = 0;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)keyboardWillShow:(NSNotification *)notif
{
// Keyboard will be shown now. Depending on which textfield is active, move up or move down the view appropriately
if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
{
[self setViewMovedUp:YES];
}
else if (![currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y < kMin)
{
[self setViewMovedUp:NO];
}
}
- (void)keyboardWillHide:(NSNotification *)notif
{
// Keyboard will be shown now. Depending on which textfield is active, move up or move down the view appropriately
if (self.view.frame.origin.y < 0 ) {
[self setViewMovedUp:NO];
}
}
- (void)viewWillAppear:(BOOL)animated
{
// Register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:self.view.window];
}
- (void)viewWillDisappear:(BOOL)animated
{
// Unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}

RPDP's code successfully moves the text field out of the way of the keyboard. But when you scroll to the top after using and dismissing the keyboard, the top has been scrolled up out of the view. This is true for the Simulator and the device. To read the content at the top of that view, one has to reload the view.
Isn't his following code supposed to bring the view back down?
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}

To bring back to original view state, add:
-(void)textFieldDidEndEditing:(UITextField *)sender
{
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}

I'm not sure if moving the view up is the correct approach. I did it in a different way, resizing the UIScrollView. I explained it in details in a little article.

Try this short trick.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}

There so many solutions, but I've spend some hours before it start works. So, I put this code here (just paste to the project, any modifications needn't):
#interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
UITextField* activeField;
UIScrollView *scrollView;
}
#end
- (void)viewDidLoad
{
[super viewDidLoad];
scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
//scrool view must be under main view - swap it
UIView* natView = self.view;
[self setView:scrollView];
[self.view addSubview:natView];
CGSize scrollViewContentSize = self.view.frame.size;
[scrollView setContentSize:scrollViewContentSize];
[self registerForKeyboardNotifications];
}
- (void)viewDidUnload {
activeField = nil;
scrollView = nil;
[self unregisterForKeyboardNotifications];
[super viewDidUnload];
}
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShown:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
-(void)unregisterForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect frame = self.view.frame;
frame.size.height -= kbSize.height;
CGPoint fOrigin = activeField.frame.origin;
fOrigin.y -= scrollView.contentOffset.y;
fOrigin.y += activeField.frame.size.height;
if (!CGRectContainsPoint(frame, fOrigin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
[scrollView setContentOffset:CGPointZero animated:YES];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
P.S: I hope the code help somebody make desired effect quickly.
(Xcode 4.5)

#user271753
To get your view back to original add:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
[self setViewMovedUp:NO];
return YES;
}

It doesn't require a scroll view to be able to move the view frame. You can change the frame of a viewcontroller's view so that the entire view moves up just enough to put the firstresponder text field above the keyboard. When I ran into this problem I created a subclass of UIViewController that does this. It observes for the keyboard will appear notification and finds the first responder subview and (if needed) it animates the main view upward just enough so that the first responder is above the keyboard. When the keyboard hides, it animates the view back where it was.
To use this subclass make your custom view controller a subclass of GMKeyboardVC and it inherits this feature (just be sure if you implement viewWillAppear and viewWillDisappear they must call super). The class is on github.

Swift 4 .
You Can Easily Move Up And Down UITextField Or UIView With UIKeyBoard With Animation
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var textField: UITextField!
#IBOutlet var chatView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
textField.resignFirstResponder()
}
#objc func keyboardWillChange(notification: NSNotification) {
let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let deltaY = targetFrame.origin.y - curFrame.origin.y
print("deltaY",deltaY)
UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
},completion: nil)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}

Here is the hack solution I came up with for a specific layout. This solution is similar to Matt Gallagher solution in that is scrolls a section into view. I am still new to iPhone development, and am not familiar with how the layouts work. Thus, this hack.
My implementation needed to support scrolling when clicking in a field, and also scrolling when the user selects next on the keyboard.
I had a UIView with a height of 775. The controls are spread out basically in groups of 3 over a large space. I ended up with the following IB layout.
UIView -> UIScrollView -> [UI Components]
Here comes the hack
I set the UIScrollView height to 500 units larger then the actual layout (1250). I then created an array with the absolute positions I need to scroll to, and a simple function to get them based on the IB Tag number.
static NSInteger stepRange[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};
NSInteger getScrollPos(NSInteger i) {
if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
return 0 ;
return stepRange[i] ;
}
Now all you need to do is use the following two lines of code in textFieldDidBeginEditing and textFieldShouldReturn (the latter one if you are creating a next field navigation)
CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
An example.
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
}
else{
[textField resignFirstResponder];
}
return YES ;
}
This method does not 'scroll back' as other methods do. This was not a requirement. Again this was for a fairly 'tall' UIView, and I did not have days to learn the internal layout engines.

As per the docs, as of iOS 3.0, the UITableViewController class automatically resizes and repositions its table view when there is in-line editing of text fields. I think it's not sufficient to put the text field inside a UITableViewCell as some have indicated.
From the docs:
A table view controller supports inline editing of table view rows;
if, for example, rows have embedded text fields in editing mode, it
scrolls the row being edited above the virtual keyboard that is
displayed.

Here I found the simplest solution to handle keypad.
You need to just copy-paste below sample code and change your textfield or any view which you want to move up.
Step-1
Just copy-paste below two method in your controller
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)deregisterFromKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Step-2
register & deregister Keypad Notifications in viewWillAppear and
viewWillDisappear methods respectively.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self registerForKeyboardNotifications];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self deregisterFromKeyboardNotifications];
[super viewWillDisappear:animated];
}
Step-3
Here comes the soul part, Just replace your textfield, and change
height how much you want to move upside.
- (void)keyboardWasShown:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//you need replace your textfield instance here
CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;
CGRect visibleRect = self.view.frame;
visibleRect.size.height -= currentKeyboardSize.height;
if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
{
//you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below
CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification *)notification
{
[self.scrollView setContentOffset:CGPointZero animated:YES];
}
Reference:
well, Please appreciate this guy, who shared this beautiful code snip, clean solution.
Hope this would be surly helpful someone out there.

Been searching for a good tutorial for beginners on the subject, found the best tutorial here.
In the MIScrollView.h example at the bottom of the tutorial be sure to put a space at
#property (nonatomic, retain) id backgroundTapDelegate;
as you see.

When UITextField is in a UITableViewCell scrolling should be setup automatically.
If it is not it is probably because of incorrect code/setup of the tableview.
For example when i reloaded my long table with one UITextField at the bottom as follows,
-(void) viewWillAppear:(BOOL)animated
{
[self.tableview reloadData];
}
then my textfield at the bottom was obscured by the keyboard which appeared when I clicked inside the textfield.
To fix this I had to do this -
-(void) viewWillAppear:(BOOL)animated
{
//add the following line to fix issue
[super viewWillAppear:animated];
[self.tableview reloadData];
}

Use this third party you don't need to write even one line
https://github.com/hackiftekhar/IQKeyboardManager
download project and drag and drop IQKeyboardManager in your project.
If you find any issue please read README document.
Guys really its remove headache to manage keyboard.

Note: this answer assumes your textField is in a scrollView.
I prefer to deal with this using scrollContentInset and scrollContentOffset instead of messing with the frames of my view.
First let's listen for the keyboard notifications
//call this from viewWillAppear
-(void)addKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
[[NSNotificationCenter default
Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Next step is to keep a property that represents the current first responder (UITextfield/ UITextVIew that currently has the keyboard).
We use the delegate methods to set this property. If you're using another component, you will need something similar.
Note that for textfield we set it in didBeginEditing and for textView in shouldBeginEditing. This is because textViewDidBeginEditing gets called after UIKeyboardWillShowNotification for some reason.
-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
self.currentFirstResponder = textView;
return YES;
}
-(void)textFieldDidBeginEditing:(UITextField *)textField{
self.currentFirstResponder = textField;
}
Finally, here's the magic
- (void)keyboardWillShow:(NSNotification*)aNotification{
NSDictionary* info = [aNotification userInfo];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
/*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
if(self.currentFirstResponder){
//keyboard origin in currentFirstResponderFrame
CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];
float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);
//only scroll the scrollview if keyboard overlays the first responder
if(spaceBetweenFirstResponderAndKeyboard>0){
//if i call setContentOffset:animate:YES it behaves differently, not sure why
[UIView animateWithDuration:0.25 animations:^{
[self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
}];
}
}
//set bottom inset to the keyboard height so you can still scroll the whole content
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
_scrollView.contentInset = contentInsets;
_scrollView.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillHide:(NSNotification*)aNotification{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
_scrollView.contentInset = contentInsets;
_scrollView.scrollIndicatorInsets = contentInsets;
}

This is the solution using Swift.
import UIKit
class ExampleViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var textField3: UITextField!
#IBOutlet var textField4: UITextField!
#IBOutlet var textField5: UITextField!
var activeTextField: UITextField!
// MARK: - View
override func viewDidLoad() {
super.viewDidLoad()
self.textField1.delegate = self
self.textField2.delegate = self
self.textField3.delegate = self
self.textField4.delegate = self
self.textField5.delegate = self
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.registerForKeyboardNotifications()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.unregisterFromKeyboardNotifications()
}
// MARK: - Keyboard
// Call this method somewhere in your view controller setup code.
func registerForKeyboardNotifications() {
let center: NSNotificationCenter = NSNotificationCenter.defaultCenter()
center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}
func unregisterFromKeyboardNotifications () {
let center: NSNotificationCenter = NSNotificationCenter.defaultCenter()
center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
// Called when the UIKeyboardDidShowNotification is sent.
func keyboardWasShown (notification: NSNotification) {
let info : NSDictionary = notification.userInfo!
let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
var aRect = self.view.frame
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
}
}
// Called when the UIKeyboardWillHideNotification is sent
func keyboardWillBeHidden (notification: NSNotification) {
let contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
// MARK: - Text Field
func textFieldDidBeginEditing(textField: UITextField) {
self.activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
self.activeTextField = nil
}
}

Related

How can I prevent the keyboard from covering text in a UITextView?

I have a UITextView in a ViewController that covers the whole screen but when I run the app and am typing in it, if I type down past the keyboard then it won't scroll up. It will only scroll up if I type all the way down to the very bottom of the screen, but if I type past where the keyboard is then the TextView won't scroll and whatever I'm typing is hidden. How can I work around this?
You can change the frame of the textview so that it's not underneath the keyboard.
You can use this code to react to the keyboard being displayed and to get the size of the keyboard.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
- (void)keyboardWasShown:(NSNotification *)notification
{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
int height = MIN(keyboardSize.height,keyboardSize.width);
int width = MAX(keyboardSize.height,keyboardSize.width);
//Place code here to resize textview
}
IQKeyboardManager was built to created this exact problem. It requires just one line of code:
IQKeyboardManager.sharedManager().enable = true
I'd highly recommend using it instead of developing your own solution (albeit good practice, but extremely time-consuming).
You can try this
These are delegate methods for for textfield.
Please ensure you included <UITextFieldDelegate> in .h file.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}

iOS: Embedded tableview doesn't "scroll up" when entering a value into textfield

I have an embedded tableview within a view. The top rows of this embedded table view are always visible, even with the keyboard. If you enter something into the lower cells I want them to scroll up (as usual) in order to see what you're entering into the textfields. However this doesn't happen and they remain hidden behind the keyboard. How can I change this?
Thx
Michael
Here is a link with a detailed explanation to achieve this. It explains the method on UIScrollView, which will also work on UITableView.
If you had used a UITableViewController instead of manually adding UITableView on a view, you would had got the behavior automatically.
You have to scroll to the position between the TextField and Tableview, check this answer.
You don't get that behavior for free, you need to scroll the content up/down yourself. A UITableView inherits from a UIScrollView, so scrolling is not the actual problem. To scroll, you can set its contentSize and contentOffset (even animated if you like).
The first thing is, you want to listen to these notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
These will tell you whether the keyboard will show or hide. In each of these methods, change the contentOffset and contentSize of your UITableView accordingly:
- (void)keyboardWillShow:(NSNotification *)notification
{
// keyboard info
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIViewAnimationOptions animationCurve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
NSTimeInterval animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// offsets
UIView *cell = myTextField.superView; // assuming your text field is a direct child of the UITableViewCell
CGFloat textFieldBottomY = self.view.frame.origin.y + myTableView.frame.origin.y - myTableView.contentOffset.y + cell.frame.origin.y + myTextField.origin.y + myTextField.frame.size.height;
CGFloat keyboardTopY = keyboardRect.origin.y;
// new content size: add size to be able to scroll
originalContentOffset = myTableView.contentOffset; // store the original content offset to set back when keyboard hides
originalContentSize = myTableView.contentSize; // store the original content size to set back when keyboard hides
CGSize newContentSize = myTableView.contentSize;
newContentSize.height += keyboardRect.size.height;
myTableView.contentSize = newContentSize;
// scroll to just beneath your text field
[UIView animateWithDuration:animationDuration delay:0 options:animationCurve animations:^{
myTableView.contentOffset = CGPointMake(0, textFieldBottomY - keyboardTopY);
} completion:NULL];
}
To reset everything:
- (void)keyboardWillHide:(NSNotification *)notification
{
// keyboard info
NSDictionary *userInfo = [notification userInfo];
UIViewAnimationOptions animationCurve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
NSTimeInterval animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// move content
[UIView animateWithDuration:animationDuration delay:0 options:animationCurve animations:^{
myTableView.contentOffset = originalContentOffset;
} completion:^(BOOL finished) {
if (finished) {
// reset content size
myTableView.contentSize = originalContentSize;
}
}];
}
To detect which text field is selected/active:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
myTextField = textField;
}
I didn't test out the code on a UITableView, but I used it very recently on a UIScrollView. Again - since a UITableView inherits from a UIScrollVIew, this should not be a problem. If the table view does not scroll correctly to just beneath the text field, the value of textFieldBottomY is incorrect.

Trying to move the view up when the keyboard is showing

I trying to push my view up when the keyboard is showing (it overlays data I want the user to see while he's typing. I'm using this code:
KBKeyboardHandler.h:
#protocol KBKeyboardHandlerDelegate;
#interface KBKeyboardHandler : NSObject
- (id)init;
// Put 'weak' instead of 'assign' if you use ARC
#property(nonatomic, assign) id<KBKeyboardHandlerDelegate> delegate;
#property(nonatomic) CGRect frame;
#end
KBKeyboardHandler.m:
#import "KBKeyboardHandler.h"
#import "KBKeyboardHandlerDelegate.h"
#implementation KBKeyboardHandler
- (id)init
{
self = [super init];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#synthesize delegate;
#synthesize frame;
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect oldFrame = self.frame;
[self retrieveFrameFromNotification:notification];
if (oldFrame.size.height != self.frame.size.height)
{
CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
self.frame.size.height - oldFrame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
}
- (void)keyboardWillHide:(NSNotification *)notification
{
if (self.frame.size.height > 0.0)
{
[self retrieveFrameFromNotification:notification];
CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
self.frame = CGRectZero;
}
- (void)retrieveFrameFromNotification:(NSNotification *)notification
{
CGRect keyboardRect;
[[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil];
}
- (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
UIViewAnimationCurve curve;
[[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];
NSTimeInterval duration;
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];
void (^action)(void) = ^{
[self.delegate keyboardSizeChanged:delta];
};
[UIView animateWithDuration:duration
delay:0.0
options:curve
animations:action
completion:nil];
}
#end
KBKeyboardHandlerDelegate.h:
#protocol KBKeyboardHandlerDelegate
- (void)keyboardSizeChanged:(CGSize)delta;
#end
Sample MyViewController.h:
#interface MyViewController : UIViewController<KBKeyboardHandlerDelegate>
...
#end
Sample MyViewController.m:
#implementation MyViewController
{
KBKeyboardHandler *keyboard;
}
- (void)dealloc
{
keyboard.delegate = nil;
[keyboard release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
keyboard = [[KBKeyboardHandler alloc] init];
keyboard.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
keyboard.delegate = nil;
[keyboard release];
keyboard = nil;
}
- (void)keyboardSizeChanged:(CGSize)delta
{
// Resize / reposition your views here. All actions performed here
// will appear animated.
// delta is the difference between the previous size of the keyboard
// and the new one.
// For instance when the keyboard is shown,
// delta may has width=768, height=264,
// when the keyboard is hidden: width=-768, height=-264.
// Use keyboard.frame.size to get the real keyboard size.
// Sample:
CGRect frame = self.view.frame;
frame.size.height -= delta.height;
self.view.frame = frame;
}
That I've found here. I'm trying to figure out why my view isn't pushing up. The keyboard is showing and keyboardSizeChanged is fired up but the view doesn't move. I opened a new project and copied the KBKeyboardHandler and delegate files to it, implemented the code and in the new project it works fine so I know for a fact that it's something in my original project that is messed up. Any idea what it can be?
If you are not using a UITableView use UIScrollView as the parent view of your elements and add the code below
Adjust as needed,
You will need a NSArray of NSObjects, I called mine scrollToObjects and in viewDidLoad add the elements to the array that would need scrolling so that when keyboardWasShown is called you can check to see if the currently selected element should be scrolled to.
I used self.activeField to store my currently selected element which is set when a element triggers an event(the event needs to be one of the first called and that is always called)
Then I check to see if the scrollToObjects contains activeField and if it does scroll the UIScrollView up and then back down when the activeField resignsFirstResponder
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
// Wouldn't go true so used an array that contains text fields that need to be scrolled to
// if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
// CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y- kbSize.height);
// [self.scrollView setContentOffset:scrollPoint animated:YES];
// }
if ([self.scrollToObjects containsObject:self.activeField]) {
CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y+ (self.activeField.frame.size.height*2)-kbSize.height);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
// self.scrollView.contentInset = UIEdgeInsetsZero;
[self.scrollView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
A simpler solution would be to handle the UITextFieldDelegate methods below with following code or something similar to activation and deactivation of the object
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
CGPoint scrollPoint = CGPointMake(0.0, textField.frame.origin.y);
[self.theScrollView setContentOffset:scrollPoint animated:YES];
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
[self.theScrollView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
self.theScrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
I'd check your calculation for delta.
CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
self.frame.size.height - oldFrame.size.height);
My guess is oldFrame and self.frame might have the same dimensions.
I'm trying to think if there's anything else that could have been missed, like properly linking views to these objects or something in the interface editor?
The problem is in keyboardSizeChanged: - what you want to adjust is frame.origin.y, not frame.size.height. At least that is what I wanted it to do.
You can also try another library, it's called TPKeyboardAvoiding. It is working great, and is very easy to setup:
Add a UIScrollView into your view controller's xib
Set the scroll view's class to TPKeyboardAvoidingScrollView (still
in the xib, via the identity inspector)
Place all your controls within that scrollview
It also automatically hooks up "Next" buttons on the keyboard to switch through the text fields.

How to move view up when a UIPickerView is displayed

I have a mainview with all of my textfields and buttons. For my textfields i am using the inputView to display UIPickerViews instead of the keyboards. I am wondering how I can main the view move up when a textfield is selected that way the picker and the pickers toolbar does not cover up the textfield, because i have some textfields at the bottom that get covered by it. I have tried using the following code from a tutorial with a table view, but it does not work for me. It builds without error, but it does not work right. The view just disappears and then it only comes back down half way when the pickerView is dismissed.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pickerShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pickerHidden:) name:UIKeyboardWillHideNotification object:nil];
}
-(void)pickerShown:(NSNotification *)note {
CGRect pickerFrame;
[[[note userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&pickerFrame];
CGRect scrollViewFrame = mainView.frame;
scrollViewFrame.size.height -= pickerFrame.size.height;
[mainView setFrame:pickerFrame];
}
-(void)pickerHidden:(NSNotification*)note{
[mainView setFrame:self.view.bounds];
}
Is this close to what i need to be doing?
May I suggest having a look at this tutorial "Sliding UITextFields around to avoid the keyboard".
In your case, you need to put the code they have in textFieldDidBeginEditing in your pickerShown method, and change the constants that they have for keyboard heights for the picker heights instead.
Hope this helps :)
For those looking for alternative solution, here's one. I made it into a library for repeated use in complex project.
/* In Keyboard.m */
static NSUInteger verticalOffset = 0;
+ (void)moveViewForKeyboard:(UITextField *)theTextField inView:(UIView *)view
{
/* Move 200 for keyboard, change the number for other types */
[self moveViewUp:view withOffset:theTextField.frame.origin.y - 200];
}
+ (void)moveViewUp:(UIView *)view withOffset:(int)offset
{
if(offset < 0)
offset = 0;
if(offset != verticalOffset)
{
[self moveView:view withOffset:offset - verticalOffset];
verticalOffset = offset;
}
}
+ (void)moveViewOnEndEditing:(UIView *)view
{
if(verticalOffset != 0)
{
[self moveView:view withOffset:-verticalOffset];
verticalOffset = 0;
}
}
+ (void)moveView:(UIView *)view withOffset:(int)offset
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
CGRect rect = view.frame;
rect.origin.y -= offset;
rect.size.height += offset;
view.frame = rect;
[UIView commitAnimations];
}
And use it this way in the views:
- (void)textFieldDidBeginEditing:(UITextField *)theTextField
{
[Keyboard moveViewForKeyboard:theTextField inView:self.view];
}
- (void)textFieldDidEndEditing:(UITextField *)theTextField
{
[Keyboard moveViewOnEndEditing:self.view];
}

How can I make a UITextField move up when the keyboard is present - on starting to edit?

With the iOS SDK:
I have a UIView with UITextFields that bring up a keyboard. I need it to be able to:
Allow scrolling of the contents of the UIScrollView to see the other text fields once the keyboard is brought up
Automatically "jump" (by scrolling up) or shortening
I know that I need a UIScrollView. I've tried changing the class of my UIView to a UIScrollView, but I'm still unable to scroll the textboxes up or down.
Do I need both a UIView and a UIScrollView? Does one go inside the other?
What needs to be implemented in order to automatically scroll to the active text field?
Ideally as much of the setup of the components as possible will be done in Interface Builder. I'd like to only write code for what needs it.
Note: the UIView (or UIScrollView) that I'm working with is brought up by a tabbar (UITabBar), which needs to function as normal.
I am adding the scroll bar just for when the keyboard comes up. Even though it's not needed, I feel like it provides a better interface because then the user can scroll and change textboxes, for example.
I've got it working where I change the frame size of the UIScrollView when the keyboard goes up and down. I'm simply using:
-(void)textFieldDidBeginEditing:(UITextField *)textField {
//Keyboard becomes visible
scrollView.frame = CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50); // Resize
}
-(void)textFieldDidEndEditing:(UITextField *)textField {
// Keyboard will hide
scrollView.frame = CGRectMake(scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height + 215 - 50); // Resize
}
However, this doesn't automatically "move up" or center the lower text fields in the visible area, which is what I would really like.
You will only need a ScrollView if the contents you have now do not fit in the iPhone screen. (If you are adding the ScrollView as the superview of the components just to make the TextField scroll up when keyboard comes up, then it's not needed.)
The standard way to prevent the TextFields from being covered by the keyboard is to move the view up/down whenever the keyboard is shown.
Here is some sample code:
#define kOFFSET_FOR_KEYBOARD 80.0
-(void)keyboardWillShow {
// Animate the current view out of the way
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)keyboardWillHide {
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
else if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if ([sender isEqual:mailTf])
{
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y >= 0)
{
[self setViewMovedUp:YES];
}
}
}
//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3]; // if you want to slide up the view
CGRect rect = self.view.frame;
if (movedUp)
{
// 1. move the view's origin up so that the text field that will be hidden come above the keyboard
// 2. increase the size of the view so that the area behind the keyboard is covered up.
rect.origin.y -= kOFFSET_FOR_KEYBOARD;
rect.size.height += kOFFSET_FOR_KEYBOARD;
}
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
I was also having a lot of issue with a UIScrollView composing of multiple UITextFields, of which, one or more of them would get obscured by the keyboard when they are being edited.
Here are some things to consider if your UIScrollView is not properly scrolling.
1) Ensure that your contentSize is greater than the UIScrollView frame size. The way to understand UIScrollViews is that the UIScrollView is like a viewing window on the content defined in the contentSize. So when in order for the UIScrollview to scroll anywhere, the contentSize must be greater than the UIScrollView. Else, there is no scrolling required as everything defined in the contentSize is already visible. BTW, default contentSize = CGSizeZero.
2) Now that you understand that the UIScrollView is really a window into your "content", the way to ensure that the keyboard is not obscuring your UIScrollView's viewing "window" would be to resize the UIScrollView so that when the keyboard is present, you have the UIScrollView window sized to just the original UIScrollView frame.size.height minus the height of the keyboard. This will ensure that your window is only that small viewable area.
3) Here's the catch: When I first implemented this I figured I would have to get the CGRect of the edited textfield and call UIScrollView's scrollRecToVisible method. I implemented the UITextFieldDelegate method textFieldDidBeginEditing with the call to the scrollRecToVisible method. This actually worked with a strange side effect that the scrolling would snap the UITextField into position. For the longest time I couldn't figure out what it was. Then I commented out the textFieldDidBeginEditing Delegate method and it all work!!(???). As it turned out, I believe the UIScrollView actually implicitly brings the currently edited UITextField into the viewable window implicitly. My implementation of the UITextFieldDelegate method and subsequent call to the scrollRecToVisible was redundant and was the cause of the strange side effect.
So here are the steps to properly scroll your UITextField in a UIScrollView into place when the keyboard appears.
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:self.view.window];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:self.view.window];
keyboardIsShown = NO;
//make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
CGSize scrollContentSize = CGSizeMake(320, 345);
self.scrollView.contentSize = scrollContentSize;
}
- (void)keyboardWillHide:(NSNotification *)n
{
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// resize the scrollview
CGRect viewFrame = self.scrollView.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
viewFrame.size.height += (keyboardSize.height - kTabBarHeight);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.scrollView setFrame:viewFrame];
[UIView commitAnimations];
keyboardIsShown = NO;
}
- (void)keyboardWillShow:(NSNotification *)n
{
// This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown. This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`. If we were to resize the `UIScrollView` again, it would be disastrous. NOTE: The keyboard notification will fire even when the keyboard is already shown.
if (keyboardIsShown) {
return;
}
NSDictionary* userInfo = [n userInfo];
// get the size of the keyboard
CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
// resize the noteView
CGRect viewFrame = self.scrollView.frame;
// I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.scrollView setFrame:viewFrame];
[UIView commitAnimations];
keyboardIsShown = YES;
}
Register for the keyboard notifications at viewDidLoad
Unregister for the keyboard nofitications at viewDidUnload
Ensure that the contentSize is set and greater than your UIScrollView at viewDidLoad
Shrink the UIScrollView when the keyboard is present
Revert back the UIScrollView when the keyboard goes away.
Use an ivar to detect if the keyboard is already shown on the screen since the keyboard notifications are sent each time a UITextField is tabbed even if the keyboard is already present to avoid shrinking the UIScrollView when it's already shrunk
One thing to note is that the UIKeyboardWillShowNotification will fire even when the keyboard is already on the screen when you tab on another UITextField. I took care of this by using an ivar to avoid resizing the UIScrollView when the keyboard is already on the screen. Inadvertently resizing the UIScrollView when the keyboard is already there would be disastrous!
Hope this code saves some of you a lot of headache.
It's actually best just to use Apple's implementation, as provided in the docs. However, the code they provide is faulty. Replace the portion found in keyboardWasShown: just below the comments to the following:
NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);
CGPoint origin = activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
[backScrollView setContentOffset:scrollPoint animated:YES];
}
The problems with Apple's code are these:
(1) They always calculate if the point is within the view's frame, but it's a ScrollView, so it may already have scrolled and you need to account for that offset:
origin.y -= scrollView.contentOffset.y
(2) They shift the contentOffset by the height of the keyboard, but we want the opposite (we want to shift the contentOffset by the height that is visible on the screen, not what isn't):
activeField.frame.origin.y-(aRect.size.height)
In textFieldDidBeginEditting and in textFieldDidEndEditing call the function [self animateTextField:textField up:YES] like so:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField:textField up:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField:textField up:NO];
}
-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
const int movementDistance = -130; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? movementDistance : -movementDistance);
[UIView beginAnimations: #"animateTextField" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
I hope this code will help you.
Swift 5
func animateTextField(textField: UITextField, up: Bool) {
let movementDistance: CGFloat = -130
let movementDuration: Double = 0.3
var movement:CGFloat = 0
if up {
movement = movementDistance
} else {
movement = -movementDistance
}
UIView.animate(withDuration: movementDuration, delay: 0, options: [.beginFromCurrentState]) {
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
animateTextField(textField: textField, up: true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
animateTextField(textField: textField, up: false)
}
Just using TextFields:
1a) Using Interface Builder: Select All TextFields => Edit => Embed In => ScrollView
1b) Manually embed TextFields in UIScrollView called scrollView
2) Set UITextFieldDelegate
3) Set each textField.delegate = self; (or make connections in Interface Builder)
4) Copy / Paste:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
[scrollView setContentOffset:scrollPoint animated:YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
[scrollView setContentOffset:CGPointZero animated:YES];
}
For Universal Solution, Here was my approach for implementing IQKeyboardManager.
Step1:- I Added global notifications of UITextField, UITextView, and UIKeyboard in a singleton class. I call it IQKeyboardManager.
Step2:- If found UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotification or UITextViewTextDidBeginEditingNotification notifications, I try to get topMostViewController instance from the UIWindow.rootViewController hierarchy. In order to properly uncover UITextField/UITextView on it, topMostViewController.view's frame needs to be adjusted.
Step3:- I calculated expected move distance of topMostViewController.view with respect to first responded UITextField/UITextView.
Step4:- I moved topMostViewController.view.frame up/down according to the expected move distance.
Step5:- If found UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotification or UITextViewTextDidEndEditingNotification notification, I again try to get topMostViewController instance from the UIWindow.rootViewController hierarchy.
Step6:- I calculated disturbed distance of topMostViewController.view which needs to be restored to it's original position.
Step7:- I restored topMostViewController.view.frame down according to the disturbed distance.
Step8:- I instantiated singleton IQKeyboardManager class instance on app load, so every UITextField/UITextView in the app will adjust automatically according to the expected move distance.
That's all IQKeyboardManager do for you with NO LINE OF CODE really!! only need to drag and drop related source file to project. IQKeyboardManager also support Device Orientation, Automatic UIToolbar Management, KeybkeyboardDistanceFromTextField and much more than you think.
I've put together a universal, drop-in UIScrollView, UITableView and even UICollectionView subclass that takes care of moving all text fields within it out of the way of the keyboard.
When the keyboard is about to appear, the subclass will find the subview that's about to be edited, and adjust its frame and content offset to make sure that view is visible, with an animation to match the keyboard pop-up. When the keyboard disappears, it restores its prior size.
It should work with basically any setup, either a UITableView-based interface, or one consisting of views placed manually.
Here' tis: solution for moving text fields out of the way of the keyboard
For Swift Programmers :
This will do everything for you, just put these in your view controller class and implement the UITextFieldDelegate to your view controller & set the textField's delegate to self
textField.delegate = self // Setting delegate of your UITextField to self
Implement the delegate callback methods:
func textFieldDidBeginEditing(textField: UITextField) {
animateViewMoving(true, moveValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
animateViewMoving(false, moveValue: 100)
}
// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
let movementDuration:NSTimeInterval = 0.3
let movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations( "animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration )
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
UIView.commitAnimations()
}
For Swift 4, 4.2, 5:
Change
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
to
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
Last note about this implementation: If you push another view controller onto the stack while the keyboard is shown, this will create an error where the view is returned back to its center frame but keyboard offset is not reset. For example, your keyboard is the first responder for nameField, but then you push a button that pushes your Help View Controller onto your stack. To fix the offset error, make sure to call nameField.resignFirstResponder() before leaving the view controller, ensuring that the textFieldDidEndEditing delegate method is called as well. I do this in the viewWillDisappear method.
There are already a lot of answers, but still none of the solutions above had all the fancy positioning stuff required for a "perfect" bug-free, backwards compatible and flicker-free animation. (bug when animating frame/bounds and contentOffset together, different interface orientations, iPad split keyboard, ...)
Let me share my solution:
(assuming you have set up 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, e.g. 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];
}
Shiun said "As it turned out, I believe the UIScrollView actually implicitly brings the currently edited UITextField into the viewable window implicitly" This seems to be true for iOS 3.1.3, but not 3.2, 4.0, or 4.1. I had to add an explicit scrollRectToVisible in order to make the UITextField visible on iOS >= 3.2.
One thing to consider is whether you ever want to use a UITextField on its own. I haven’t come across any well-designed iPhone apps that actually use UITextFields outside of UITableViewCells.
It will be some extra work, but I recommend you implement all data entry views a table views. Add a UITextView to your UITableViewCells.
This document details a solution to this problem. Look at the source code under 'Moving Content That Is Located Under the Keyboard'. It's pretty straightforward.
EDIT: Noticed there's a wee glitch in the example. You will probably want to listen for UIKeyboardWillHideNotification instead of UIKeyboardDidHideNotification. Otherwise the scroll view behind of the keyboard will be clipped for the duration of the keyboard closing animation.
Easiest solution found
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
A little fix that works for many UITextFields:
#pragma mark UIKeyboard handling
#define kMin 150
-(void)textFieldDidBeginEditing:(UITextField *)sender
{
if (currTextField) {
[currTextField release];
}
currTextField = [sender retain];
// Move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
[self setViewMovedUp:YES];
}
}
// Method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3]; // If you want to slide up the view
CGRect rect = self.view.frame;
if (movedUp)
{
// 1. move the view's origin up so that the text field that will be hidden come above the keyboard
// 2. increase the size of the view so that the area behind the keyboard is covered up.
rect.origin.y = kMin - currTextField.frame.origin.y ;
}
else
{
// Revert back to the normal state.
rect.origin.y = 0;
}
self.view.frame = rect;
[UIView commitAnimations];
}
- (void)keyboardWillShow:(NSNotification *)notif
{
// Keyboard will be shown now. Depending on which textfield is active, move up or move down the view appropriately
if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
{
[self setViewMovedUp:YES];
}
else if (![currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y < kMin)
{
[self setViewMovedUp:NO];
}
}
- (void)keyboardWillHide:(NSNotification *)notif
{
// Keyboard will be shown now. Depending on which textfield is active, move up or move down the view appropriately
if (self.view.frame.origin.y < 0 ) {
[self setViewMovedUp:NO];
}
}
- (void)viewWillAppear:(BOOL)animated
{
// Register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:self.view.window];
}
- (void)viewWillDisappear:(BOOL)animated
{
// Unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}
RPDP's code successfully moves the text field out of the way of the keyboard. But when you scroll to the top after using and dismissing the keyboard, the top has been scrolled up out of the view. This is true for the Simulator and the device. To read the content at the top of that view, one has to reload the view.
Isn't his following code supposed to bring the view back down?
else
{
// revert back to the normal state.
rect.origin.y += kOFFSET_FOR_KEYBOARD;
rect.size.height -= kOFFSET_FOR_KEYBOARD;
}
To bring back to original view state, add:
-(void)textFieldDidEndEditing:(UITextField *)sender
{
//move the main view, so that the keyboard does not hide it.
if (self.view.frame.origin.y < 0)
{
[self setViewMovedUp:NO];
}
}
I'm not sure if moving the view up is the correct approach. I did it in a different way, resizing the UIScrollView. I explained it in details in a little article.
Try this short trick.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
There so many solutions, but I've spend some hours before it start works. So, I put this code here (just paste to the project, any modifications needn't):
#interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
UITextField* activeField;
UIScrollView *scrollView;
}
#end
- (void)viewDidLoad
{
[super viewDidLoad];
scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
//scrool view must be under main view - swap it
UIView* natView = self.view;
[self setView:scrollView];
[self.view addSubview:natView];
CGSize scrollViewContentSize = self.view.frame.size;
[scrollView setContentSize:scrollViewContentSize];
[self registerForKeyboardNotifications];
}
- (void)viewDidUnload {
activeField = nil;
scrollView = nil;
[self unregisterForKeyboardNotifications];
[super viewDidUnload];
}
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShown:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
-(void)unregisterForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect frame = self.view.frame;
frame.size.height -= kbSize.height;
CGPoint fOrigin = activeField.frame.origin;
fOrigin.y -= scrollView.contentOffset.y;
fOrigin.y += activeField.frame.size.height;
if (!CGRectContainsPoint(frame, fOrigin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
[scrollView setContentOffset:CGPointZero animated:YES];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
P.S: I hope the code help somebody make desired effect quickly.
(Xcode 4.5)
#user271753
To get your view back to original add:
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
[self setViewMovedUp:NO];
return YES;
}
It doesn't require a scroll view to be able to move the view frame. You can change the frame of a viewcontroller's view so that the entire view moves up just enough to put the firstresponder text field above the keyboard. When I ran into this problem I created a subclass of UIViewController that does this. It observes for the keyboard will appear notification and finds the first responder subview and (if needed) it animates the main view upward just enough so that the first responder is above the keyboard. When the keyboard hides, it animates the view back where it was.
To use this subclass make your custom view controller a subclass of GMKeyboardVC and it inherits this feature (just be sure if you implement viewWillAppear and viewWillDisappear they must call super). The class is on github.
Swift 4 .
You Can Easily Move Up And Down UITextField Or UIView With UIKeyBoard With Animation
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var textField: UITextField!
#IBOutlet var chatView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
textField.resignFirstResponder()
}
#objc func keyboardWillChange(notification: NSNotification) {
let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let deltaY = targetFrame.origin.y - curFrame.origin.y
print("deltaY",deltaY)
UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
},completion: nil)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
Here is the hack solution I came up with for a specific layout. This solution is similar to Matt Gallagher solution in that is scrolls a section into view. I am still new to iPhone development, and am not familiar with how the layouts work. Thus, this hack.
My implementation needed to support scrolling when clicking in a field, and also scrolling when the user selects next on the keyboard.
I had a UIView with a height of 775. The controls are spread out basically in groups of 3 over a large space. I ended up with the following IB layout.
UIView -> UIScrollView -> [UI Components]
Here comes the hack
I set the UIScrollView height to 500 units larger then the actual layout (1250). I then created an array with the absolute positions I need to scroll to, and a simple function to get them based on the IB Tag number.
static NSInteger stepRange[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};
NSInteger getScrollPos(NSInteger i) {
if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
return 0 ;
return stepRange[i] ;
}
Now all you need to do is use the following two lines of code in textFieldDidBeginEditing and textFieldShouldReturn (the latter one if you are creating a next field navigation)
CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
An example.
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
[self.scrollView setContentOffset:point animated:YES] ;
}
else{
[textField resignFirstResponder];
}
return YES ;
}
This method does not 'scroll back' as other methods do. This was not a requirement. Again this was for a fairly 'tall' UIView, and I did not have days to learn the internal layout engines.
As per the docs, as of iOS 3.0, the UITableViewController class automatically resizes and repositions its table view when there is in-line editing of text fields. I think it's not sufficient to put the text field inside a UITableViewCell as some have indicated.
From the docs:
A table view controller supports inline editing of table view rows;
if, for example, rows have embedded text fields in editing mode, it
scrolls the row being edited above the virtual keyboard that is
displayed.
Here I found the simplest solution to handle keypad.
You need to just copy-paste below sample code and change your textfield or any view which you want to move up.
Step-1
Just copy-paste below two method in your controller
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)deregisterFromKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Step-2
register & deregister Keypad Notifications in viewWillAppear and
viewWillDisappear methods respectively.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self registerForKeyboardNotifications];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self deregisterFromKeyboardNotifications];
[super viewWillDisappear:animated];
}
Step-3
Here comes the soul part, Just replace your textfield, and change
height how much you want to move upside.
- (void)keyboardWasShown:(NSNotification *)notification
{
NSDictionary* info = [notification userInfo];
CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//you need replace your textfield instance here
CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;
CGRect visibleRect = self.view.frame;
visibleRect.size.height -= currentKeyboardSize.height;
if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
{
//you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below
CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification *)notification
{
[self.scrollView setContentOffset:CGPointZero animated:YES];
}
Reference:
well, Please appreciate this guy, who shared this beautiful code snip, clean solution.
Hope this would be surly helpful someone out there.
Been searching for a good tutorial for beginners on the subject, found the best tutorial here.
In the MIScrollView.h example at the bottom of the tutorial be sure to put a space at
#property (nonatomic, retain) id backgroundTapDelegate;
as you see.
When UITextField is in a UITableViewCell scrolling should be setup automatically.
If it is not it is probably because of incorrect code/setup of the tableview.
For example when i reloaded my long table with one UITextField at the bottom as follows,
-(void) viewWillAppear:(BOOL)animated
{
[self.tableview reloadData];
}
then my textfield at the bottom was obscured by the keyboard which appeared when I clicked inside the textfield.
To fix this I had to do this -
-(void) viewWillAppear:(BOOL)animated
{
//add the following line to fix issue
[super viewWillAppear:animated];
[self.tableview reloadData];
}
Use this third party you don't need to write even one line
https://github.com/hackiftekhar/IQKeyboardManager
download project and drag and drop IQKeyboardManager in your project.
If you find any issue please read README document.
Guys really its remove headache to manage keyboard.
Note: this answer assumes your textField is in a scrollView.
I prefer to deal with this using scrollContentInset and scrollContentOffset instead of messing with the frames of my view.
First let's listen for the keyboard notifications
//call this from viewWillAppear
-(void)addKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
[[NSNotificationCenter default
Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Next step is to keep a property that represents the current first responder (UITextfield/ UITextVIew that currently has the keyboard).
We use the delegate methods to set this property. If you're using another component, you will need something similar.
Note that for textfield we set it in didBeginEditing and for textView in shouldBeginEditing. This is because textViewDidBeginEditing gets called after UIKeyboardWillShowNotification for some reason.
-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
self.currentFirstResponder = textView;
return YES;
}
-(void)textFieldDidBeginEditing:(UITextField *)textField{
self.currentFirstResponder = textField;
}
Finally, here's the magic
- (void)keyboardWillShow:(NSNotification*)aNotification{
NSDictionary* info = [aNotification userInfo];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
/*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
if(self.currentFirstResponder){
//keyboard origin in currentFirstResponderFrame
CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];
float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);
//only scroll the scrollview if keyboard overlays the first responder
if(spaceBetweenFirstResponderAndKeyboard>0){
//if i call setContentOffset:animate:YES it behaves differently, not sure why
[UIView animateWithDuration:0.25 animations:^{
[self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
}];
}
}
//set bottom inset to the keyboard height so you can still scroll the whole content
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
_scrollView.contentInset = contentInsets;
_scrollView.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillHide:(NSNotification*)aNotification{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
_scrollView.contentInset = contentInsets;
_scrollView.scrollIndicatorInsets = contentInsets;
}
This is the solution using Swift.
import UIKit
class ExampleViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var textField3: UITextField!
#IBOutlet var textField4: UITextField!
#IBOutlet var textField5: UITextField!
var activeTextField: UITextField!
// MARK: - View
override func viewDidLoad() {
super.viewDidLoad()
self.textField1.delegate = self
self.textField2.delegate = self
self.textField3.delegate = self
self.textField4.delegate = self
self.textField5.delegate = self
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.registerForKeyboardNotifications()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.unregisterFromKeyboardNotifications()
}
// MARK: - Keyboard
// Call this method somewhere in your view controller setup code.
func registerForKeyboardNotifications() {
let center: NSNotificationCenter = NSNotificationCenter.defaultCenter()
center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}
func unregisterFromKeyboardNotifications () {
let center: NSNotificationCenter = NSNotificationCenter.defaultCenter()
center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
// Called when the UIKeyboardDidShowNotification is sent.
func keyboardWasShown (notification: NSNotification) {
let info : NSDictionary = notification.userInfo!
let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size
let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your app might not need or want this behavior.
var aRect = self.view.frame
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
}
}
// Called when the UIKeyboardWillHideNotification is sent
func keyboardWillBeHidden (notification: NSNotification) {
let contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
// MARK: - Text Field
func textFieldDidBeginEditing(textField: UITextField) {
self.activeTextField = textField
}
func textFieldDidEndEditing(textField: UITextField) {
self.activeTextField = nil
}
}

Resources