textFieldDidBeginEditing is called prematurely

2019-04-07 08:01发布

问题:

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...

回答1:

May I suggest a GitHub repository

https://github.com/hackiftekhar/IQKeyboardManager



回答2:

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


回答3:

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.



回答4:

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.



回答5:

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.