7

I have an application in which I have to scroll up in case of the keyboard showing. to get the keyboard size, I'm registering the UIKeyboardWillShowNotification event like so:

   [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(keyboardWillShow:)
     name:UIKeyboardWillShowNotification
     object:self.view.window]

This does work, the problem is, it is being called after the textFieldDidBeginEditing was called. So, I can't get the actual keyboard size, but only after the field is already in edit mode, which defeats the whole purpose of registering this event on the first place. I'm sure I've called the UIKeyboardWillShowNotification and not the UIKeyboardDidShowNotification, although switching these two yield the same results: first call was made to the delegate method and only then to the notification method. Any idea on how to turn this around? Currently I'm hard coding the size, which is very bad practice...

donald
  • 489
  • 5
  • 13
  • Have you got solution for this? – Baby Groot Dec 02 '13 at 11:38
  • I'm dealing with this issue too.. – Rizon Jan 13 '14 at 15:52
  • Am I correct in assuming that, when the keyboard notification fires, you need to know which UITextField is being edited, so that you can scroll correctly? If that's the case, then perhaps you could stash a reference to that UITextField in `textViewDidBeginEditing`, to be used when you receive the notification. – Joshua Kaden Jan 13 '14 at 23:00

5 Answers5

2

May I suggest a GitHub repository

https://github.com/hackiftekhar/IQKeyboardManager

Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
1

Here is a base class I have written for this use exactly.It is a subclass of UIViewController Whenever I want to implement such behaviour I just make my view controller a sub class of this base class.

BTW - You are right. textFieldDidBeginEditing is called after keyboard shows up which is why you want to scroll up in the call backs method of the keyboard as described in my class.

Also please note that for this to work you need to embed your entire view in a Scroll View and connect the IBOutlet of the scroll view to it.

If you are not using story board remove the IBOutlet part and embed your view in a scroll view and make the connection in code.

After this said here is the code:

Header File

#import <UIKit/UIKit.h>

@interface BaseViewControllerWithKeyboard : BaseViewController

@property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
@property (nonatomic, strong) UITextField *activeField;

@end

Implementation File

#import "BaseViewControllerWithKeyboard.h"

@interface BaseViewControllerWithKeyboard ()

@end

@implementation BaseViewControllerWithKeyboard

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self registerForKeyboardNotifications];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification 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:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = 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.
    CGRect aRect = self.view.frame;
    aRect.size.height -= kbSize.height;
    if (!CGRectContainsPoint(aRect, _activeField.frame.origin) ) {
        [self.scrollView scrollRectToVisible:_activeField.frame animated:YES];
    }
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}

@end
Michal Shatz
  • 1,416
  • 2
  • 20
  • 31
  • Please review this solution @Rizon – Michal Shatz Jan 15 '14 at 20:36
  • A use case that dodges this code... 1. if a UIView has multiple UITextFields and 2. only a subset of the fields need the view to be scrolled 3. then when a user moves the focus from one UITextfield to another, 4. keyboardWasShown: will not be called. – Carl Jul 03 '14 at 14:02
  • 1. Why will it not work with multiple textfields? It does 2. please explain 3. Son't see a problem in this case either. 4. KeyboardWasShown will get called if you registered to notification properly. – Michal Shatz Jul 03 '14 at 14:05
  • E.g., 1. user taps first UITextField and keyboardWasShown: is called. 2. keyboard is displayed and UIView is scrolled a little 3. user taps second UITextBox (let's say the keyboard overlaps the lower part of this box but the user can still tap it) 4. keyboard is visible and so keyboardWasShown: isn't called. – Carl Jul 03 '14 at 14:09
  • let's concentrate on #4. keyboardWasShown: doesn't get called when a user switches focus from one UITextField to another. Therefore it can move the view to better position the UITextField. – Carl Jul 03 '14 at 14:20
  • OK. I see. You are talking about a rear case where there is one text field which is slightly hidden by the keyboard. If you are concerned about this situation you can implement the textFieldShouldBeginEditing method and make arrangemnet there – Michal Shatz Jul 06 '14 at 06:06
  • I've no idea if this case is rare - it just happened to be my app's case where I'd laid out two UITextField vertically using Storyboard using its guidelines for spacing and bang - this was a case I had to deal with. Perhaps on a larger screen, eg iPad, where multiple boxes are visible at once, is a commoner case. – Carl Jul 07 '14 at 07:36
  • 1
    I added a handler for textFieldDidBeginEditing: which scrolls the controller's UIView if the keyboard is visible (a class bool) to make the textField visible. I needed to store the keyboard size as well (a class CGSize). – Carl Jul 07 '14 at 07:38
0

I opened a new project in XCode 5, added a UITextField to the ViewController and connected its delegate.

This is my only code:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myNotificationMethod:) name:UIKeyboardWillShowNotification object:nil];
}

- (void)myNotificationMethod:(NSNotification*)notification
{
    NSDictionary* keyboardInfo = [notification userInfo];
    NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
    CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
    NSLog(@"Rect: %@",NSStringFromCGRect(keyboardFrameBeginRect));
}

Here's the log output:

Portraid:

 Rect: {{0, 480}, {320, 216}}

Landscape:

 Rect: {{-162, 0}, {162, 480}}

Edit:

Regarding textFieldDidBeginEditing being called before name:UIKeyboardWillShowNotification, I don't really understand why there's a difference if the textField is in edit mode or not but there are a few ways to solve this.

  1. saving a reference to textField from textFieldShouldBeginEditing and use it inside myNotificationMethod if in did textFieldShouldBeginEditing was triggered.

  2. Playing around with UIResponder like so:

in textFieldDidBeginEditing -> Save a reference to the UIResponder and change the UIResponder to a temp irrelevant one. in myNotificationMethod do what ever you want to do to the textField (that is not in edit mode \ first responder), when you are done make it your main UIResponder.

Segev
  • 19,035
  • 12
  • 80
  • 152
  • That's cool, the thing is that if you'd use textfield:didBeginEditing: method you can see that it gets called before the keyboard notification is.. – Rizon Jan 14 '14 at 12:08
  • @Rizon What about saving a reference to `textField` from `textFieldShouldBeginEditing` and use it inside `myNotificationMethod` if in did `textFieldShouldBeginEditing` was triggered ? – Segev Jan 14 '14 at 12:54
  • I'm not sure I understand why there's a difference if the textField is in edit mode or not. A bit more explaining on why you'd want to do it may help. – Segev Jan 14 '14 at 12:57
0

According to the Apple documentation UIKeyboardWillShowNotification is called just before the keyboard will be shown, while UITextFieldDidBeginEditing is called just after the text field becomes first responder. The process to show the keyboard is started after the text field becomes first responder, and only if the keyboard is not already shown. This means UIKeyboardWillShowNotification will be called after UITextFieldDidBeginEditing. So UITextFieldDidBeginEditing is not called prematurely.

If you just want to scroll up so the text field won't be hidden under the keyboard, you can just set the content offset of the scroll view to the y-origin of the text field in UITextFieldShouldBeginEditing or UITextFieldDidBeginEditing.

halil_g
  • 631
  • 6
  • 11
  • "you can just set the content offset of the scroll view to the y-origin of the text field" The question is: how do you know if the UITextField needs the view's offset changing? – Carl Jul 03 '14 at 14:22
0

Old question, but I ran into the same issue today. I have built a little "dirty" workaround that does not force me to hardcode the keyboard size. I simply did the following in viewDidAppear (attention - Swift):

 override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.infoTextField.becomeFirstResponder()
    self.infoTextField.resignFirstResponder()
}

This triggers the UIKeyboardWillShowNotification and you can get the keyboard size from the notification and store it in a property. Hope this helps someone, it worked in my case.

croX
  • 1,725
  • 1
  • 19
  • 23