iPhone Keyboard Covers UITextField

87,230

Solution 1

The usual solution is to slide the field (and everything above it) up with an animation, and then back down when you are done. You may need to put the text field and some of the other items into another view and slide the view as a unit. (I call these things "plates" as in "tectonic plates", but that's just me). But here is the general idea if you don't need to get fancy.

- (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];
}

Solution 2

This worked wonders for me sliding uitextfields

In particular it has the benefit of calculating the slide animation distance depending on the position of the text field.

Solution 3

IQKeyboardManager do this for you with NO LINE OF CODE, only need to drag and drop related source file to project. IQKeyboardManager also support Device Orientation, Automatic UIToolbar Management, keyboardDistanceFromTextField and much more than you think.

enter image description here

Here is the Control Flow Chart: Control Flow Chart

Step1:- Added global notifications of UITextField, UITextView, and UIKeyboard in a singleton class. I called it IQKeyboardManager.

Step2:- If found UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotification or UITextViewTextDidBeginEditingNotification notifications, then 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:- Calculated expected move distance of topMostViewController.view with respect to first responded UITextField/UITextView.

Step4:- Moved topMostViewController.view.frame up/down according to the expected move distance.

Step5:- If found UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotification or UITextViewTextDidEndEditingNotification notification, then again try to get topMostViewController instance from the UIWindow.rootViewController hierarchy.

Step6:- Calculated disturbed distance of topMostViewController.view which needs to be restored to it's original position.

Step7:- Restored topMostViewController.view.frame down according to the disturbed distance.

Step8:- 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

Solution 4

To expand on Amagrammer answer, here is a sample class:

LoginViewController.h

@interface LoginViewController : UIViewController <UITextFieldDelegate> {

}

@property (nonatomic, retain) IBOutlet UITextField    *emailTextField;
@property (nonatomic, retain) IBOutlet UITextField    *passwordTextField;

Notice we are implementing the "UITextFieldDelegate"

LoginViewController.m

@implementation LoginViewController
@synthesize emailTextField=_emailTextField;
@synthesize passwordTextField=_passwordTextField;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        //Register to receive an update when the app goes into the backround
        //It will call our "appEnteredBackground method
        [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(appEnteredBackground)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
    }
    return self;
}


- (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];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}
//This is called when the app goes into the background.
//We must reset the responder because animations will not be saved
- (void)appEnteredBackground{
    [self.emailTextField resignFirstResponder];
    [self.passwordTextField resignFirstResponder];
}

Solution 5

I have face the same issue in UITableView textField cells. I solve this issue by implementing following method to listen the keyboard notification.

Observer for the notifications here:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];

Handle those notification by using below function:

(void)keyboardWasShown:(NSNotification*)aNotification 
(void)keyboardWillBeHidden:(NSNotification*)aNotification 
Share:
87,230
Cruinh
Author by

Cruinh

Thinking in code since '98. (Swift, Objective-C, Java, C++, Delphi, and more scripting languages than I can remember)

Updated on July 08, 2022

Comments

  • Cruinh
    Cruinh almost 2 years

    I have an app where, in Interface Builder, I set up a UIView that has a text field near the bottom of the view. When I run the app and try to enter text into that field, the keyboard slides up overtop of the field so I can't see what I'm typing until I hide the keyboard again.

    Has anyone else run into this problem and found a good way to solve it without either making the parent view scrollable or moving the text field farther up the screen?

  • jlstrecker
    jlstrecker over 12 years
    This is great. You don't have to pick some 'movement distance' constant for each text field -- it's calculated for you.
  • Marchy
    Marchy over 12 years
    Best solution by far. Phenomenal mechanism!
  • Ben Coffman
    Ben Coffman about 12 years
    Love it that you didn't post a link to code, but the code itself.
  • James
    James almost 12 years
    Great bit of code. Didn't have to edit it for it to work or anything. Thanks~
  • conecon
    conecon over 11 years
    It is useful to cover all of screen as below: const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
  • lolol
    lolol over 11 years
    No, it's not. If the user taps the first textfield it will get above the visible area.
  • xu huanze
    xu huanze over 11 years
    this does not work very well when user switches the keyboard from normal keyboard to an international keyboard, which has a different height.
  • Mathieu
    Mathieu over 11 years
    I have to mention that "If your are writing an application for iOS 4 or later, you should use the block-based methods for animating your content instead." Referenced from : developer.apple.com/library/ios/#documentation/windowsviews/‌​…
  • Khon Lieu
    Khon Lieu over 11 years
    Works great on the iPad too. I just updated PORTRAIT_KEYBOARD_HEIGHT = 264 and LANDSCAPE_KEYBOARD_HEIGHT = 352. Great link. Thanks.
  • Jared Sealey
    Jared Sealey about 11 years
    If you have multiple text fields and only want 1 to animate, add a tag in the xib file and have an if statement before the animate statements if(textField.tag == 1) [self animateTextField: textField up: YES];
  • JimmyJammed
    JimmyJammed about 11 years
    The above link just made my day! So simple to implement, works flawlessly so far!
  • john doe
    john doe almost 11 years
    This is beautiful!! You are awesome!
  • Sandy
    Sandy almost 11 years
    Works great, just copy paste almost. Good explaination as well.
  • Ken W
    Ken W almost 11 years
    This works perfectly (in terms of the animation values) for an iPhone 4S on iOS 6.1.3 (Retina 3.5" screen)
  • Security Hound
    Security Hound almost 11 years
    @NANNAV - Please provide comments when you make suggestions to modify an answer.
  • Adil Soomro
    Adil Soomro almost 11 years
    +1 for mentioning UIApplicationDidEnterBackgroundNotification, else it'll move more downward if one press Home button and come again into the app, causing it to ugly and buggy.
  • miek
    miek over 10 years
    The link is broken. Please consider including an independent solution in the future!
  • dreampowder
    dreampowder over 10 years
    Still works perfectly with iOS 7.0.3, thanks for the simple and elegant solution.
  • Renexandro
    Renexandro about 10 years
    This is best the explanation out there about this topic. Other tutorials use Table Views, Scroll Views, etc. This actually works without getting into any other complexities, plain and simple. Thanks for sharing this source.
  • Hless
    Hless about 10 years
    Still works on iOS 7.1 (iPad). The offset doesn't stick after rotating though, so you'd have to set it again in the 'didRotateFromInterfaceOrientation' method.
  • Mohd Iftekhar Qurashi
    Mohd Iftekhar Qurashi about 10 years
    This official solution is now wrapped in a control see here:- stackoverflow.com/a/17707094/1582217
  • Raj Pawan Gumdal
    Raj Pawan Gumdal almost 10 years
    Idea is good, but 'contentInset' property does not help here, coz contentInset will just provide you the padding effect: stackoverflow.com/a/10049782/260665
  • learner
    learner almost 10 years
    The accepted answer wasn't working for me. But this one does.
  • unom
    unom over 9 years
  • Mohd Iftekhar Qurashi
    Mohd Iftekhar Qurashi over 9 years
    @Raj, That may be the case which I can look on IQKeyboardManager, but still no one open any issues on the official IQKeyboardManager github repository regarding the contentInset property issue, so I assume it's working.
  • Mohd Iftekhar Qurashi
    Mohd Iftekhar Qurashi almost 9 years
    @ZaEeMZaFaR IQKeyboardManager is also optimised for iPad. Can you please open an issue on library github repo and upload a demo project demonstrating the issue with iPad?
  • ZaEeM ZaFaR
    ZaEeM ZaFaR almost 9 years
    Thanks for reply. I will do that if issue persists, before you comment I was thinking that IQKeyboardManager is not for universal device.
  • ZaEeM ZaFaR
    ZaEeM ZaFaR almost 9 years
    By Investigating more, its working fine with iPad on simulator but not on actual iPad device. Working perfectly fine on iPhone as well (Device + Simulator). What could be the issue ?
  • Mohd Iftekhar Qurashi
    Mohd Iftekhar Qurashi almost 9 years
    As I previously said, you should raise an issue on github repo with demo project with your iOS and device version details, so we can look into the issue.
  • ZaEeM ZaFaR
    ZaEeM ZaFaR over 8 years
    Thanks for the help, I was looking to post a POC where I found that 'split keyboard' was turned ON in iOS system preferences. And that was issue which stops the controller to listen keyboard notification. Worked like charm when I turned off the split keyboard.
  • Elvis Oliveira
    Elvis Oliveira over 8 years
    The accepted answer work only when you set the delegate of every UITextField. This framework works in anyone UITextField, I think this is the best solution for now.