textFieldDidBeginEditing вызывается преждевременно

У меня есть приложение, в котором мне нужно прокручивать вверх в случае отображения клавиатуры. чтобы получить размер клавиатуры, я регистрирую событие UIKeyboardWillShowNotification следующим образом:

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

Это работает, проблема в том, что он вызывается после вызова textFieldDidBeginEditing. Итак, я не могу получить фактический размер клавиатуры, но только после того, как поле уже находится в режиме редактирования, что в первую очередь противоречит всей цели регистрации этого события. Я уверен, что вызвал UIKeyboardWillShowNotification, а не UIKeyboardDidShowNotification, хотя их переключение дает одинаковые результаты: сначала был сделан вызов метода делегата, а только затем метод уведомления. Любая идея о том, как это изменить? В настоящее время я жестко кодирую размер, что является очень плохой практикой...


person donald    schedule 12.11.2012    source источник
comment
У вас есть решение для этого?   -  person Baby Groot    schedule 02.12.2013
comment
тоже занимаюсь этим вопросом..   -  person Rizon    schedule 13.01.2014
comment
Правильно ли я предполагаю, что при срабатывании уведомления клавиатуры вам нужно знать, какой UITextField редактируется, чтобы вы могли правильно прокручивать? Если это так, то, возможно, вы могли бы спрятать ссылку на этот UITextField в textViewDidBeginEditing, чтобы использовать его при получении уведомления.   -  person Joshua Kaden    schedule 14.01.2014


Ответы (5)


Могу ли я предложить репозиторий GitHub

https://github.com/hackiftekhar/IQKeyboardManager

person Warif Akhand Rishi    schedule 19.01.2014

Вот базовый класс, который я написал именно для этого использования. Это подкласс UIViewController Всякий раз, когда я хочу реализовать такое поведение, я просто делаю свой контроллер представления подклассом этого базового класса.

КСТАТИ - Вы правы. textFieldDidBeginEditing вызывается после появления клавиатуры, поэтому вы хотите прокрутить вверх в методе обратного вызова клавиатуры, как описано в моем классе.

Также обратите внимание, что для этого вам необходимо встроить все представление в представление прокрутки и подключить к нему IBOutlet представления прокрутки.

Если вы не используете раскадровку, удалите часть IBOutlet и вставьте свое представление в представление прокрутки и выполните соединение в коде.

После этого сказал вот код:

Файл заголовка

#import <UIKit/UIKit.h>

@interface BaseViewControllerWithKeyboard : BaseViewController

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

@end

Файл реализации

#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
person Michal Shatz    schedule 14.01.2014
comment
Пожалуйста, просмотрите это решение @Rizon - person Michal Shatz; 16.01.2014
comment
Вариант использования, который уклоняется от этого кода... 1. если UIView имеет несколько UITextFields и 2. только подмножество полей требует прокрутки представления 3. тогда, когда пользователь перемещает фокус с одного поля UIText на другое, 4. клавиатураWasShown: вызываться не будет. - person Carl; 03.07.2014
comment
1. Почему это не будет работать с несколькими текстовыми полями? 2. Пожалуйста, объясните 3. Сын не видит проблемы и в этом случае. 4. KeyboardWasShown будет вызываться, если вы правильно зарегистрировались на уведомление. - person Michal Shatz; 03.07.2014
comment
Например, 1. пользователь касается первого UITextField и вызывается keyboardWasShown:. 2. отображается клавиатура и UIView немного прокручивается 3. пользователь нажимает второй UITextBox (скажем, клавиатура перекрывает нижнюю часть этого поля, но пользователь все еще может нажимать на нее) 4. клавиатура видна, поэтому клавиатураWasShown: не вызывается . - person Carl; 03.07.2014
comment
давайте сосредоточимся на # 4. клавиатураWasShown: не вызывается, когда пользователь переключает фокус с одного поля UITextField на другое. Поэтому он может переместить представление, чтобы лучше расположить UITextField. - person Carl; 03.07.2014
comment
ХОРОШО. Я понимаю. Вы говорите о заднем корпусе, где есть одно текстовое поле, которое немного скрыто клавиатурой. Если вас беспокоит эта ситуация, вы можете реализовать метод textFieldShouldBeginEditing и сделать там аранжировку - person Michal Shatz; 06.07.2014
comment
Я понятия не имею, является ли этот случай редким - это был случай моего приложения, когда я выложил два UITextField вертикально, используя Storyboard, используя его рекомендации по интервалам и взрывам - это был случай, с которым мне пришлось иметь дело. Возможно, на большом экране, например, на iPad, где одновременно видны несколько блоков, это более распространенный случай. - person Carl; 07.07.2014
comment
Я добавил обработчик для textFieldDidBeginEditing: который прокручивает UIView контроллера, если клавиатура видна (класс bool), чтобы сделать textField видимым. Мне также нужно было сохранить размер клавиатуры (класс CGSize). - person Carl; 07.07.2014

Я открыл новый проект в XCode 5, добавил UITextField к ViewController и подключил его делегат.

Это мой единственный код:

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

Вот вывод журнала:

Портрет:

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

Пейзаж:

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

Изменить:

Что касается textFieldDidBeginEditing, вызываемого перед name:UIKeyboardWillShowNotification, я действительно не понимаю, почему есть разница, находится ли textField в режиме редактирования или нет, но есть несколько способов решить эту проблему.

  1. сохранение ссылки на textField из textFieldShouldBeginEditing и использование ее внутри myNotificationMethod, если в ней было запущено textFieldShouldBeginEditing.

  2. Играем с UIResponder вот так:

в textFieldDidBeginEditing -> Сохраните ссылку на UIResponder и измените UIResponder на временную нерелевантную. в myNotificationMethod делайте все, что хотите, с текстовым полем (то есть не в режиме редактирования \ первый ответчик), когда вы закончите, сделайте его своим основным UIResponder.

person Segev    schedule 14.01.2014
comment
Это круто, дело в том, что если вы используете метод textfield:didBeginEditing:, вы увидите, что он вызывается до того, как появится уведомление с клавиатуры. - person Rizon; 14.01.2014
comment
@Rizon Как насчет сохранения ссылки на textField из textFieldShouldBeginEditing и использования ее внутри myNotificationMethod, если в did textFieldShouldBeginEditing сработало? - person Segev; 14.01.2014
comment
Я не уверен, что понимаю, почему есть разница, находится ли textField в режиме редактирования или нет. Немного больше объяснений того, почему вы хотели бы сделать это, может помочь. - person Segev; 14.01.2014

Согласно документации Apple, UIKeyboardWillShowNotification вызывается непосредственно перед отображением клавиатуры, а UITextFieldDidBeginEditing вызывается сразу после того, как текстовое поле становится первым ответчиком. Процесс отображения клавиатуры запускается после, когда текстовое поле становится первым ответчиком, и только в том случае, если клавиатура еще не отображается. Это означает, что UIKeyboardWillShowNotification будет вызываться после UITextFieldDidBeginEditing. Поэтому UITextFieldDidBeginEditing не вызывается преждевременно.

Если вы просто хотите прокрутить текстовое поле вверх, чтобы текстовое поле не было скрыто под клавиатурой, вы можете просто установить смещение содержимого представления прокрутки на y-начало текстового поля в UITextFieldShouldBeginEditing или UITextFieldDidBeginEditing.

person halil_g    schedule 19.01.2014
comment
вы можете просто установить смещение содержимого представления прокрутки на y-начало текстового поля. Вопрос: как узнать, нуждается ли UITextField в изменении смещения представления? - person Carl; 03.07.2014

Старый вопрос, но сегодня я столкнулся с той же проблемой. Я создал небольшой «грязный» обходной путь, который не заставляет меня жестко задавать размер клавиатуры. Я просто сделал следующее в viewDidAppear (внимание - Swift):

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

Это активирует UIKeyboardWillShowNotification, и вы можете получить размер клавиатуры из уведомления и сохранить его в свойстве. Надеюсь, это кому-то поможет, в моем случае сработало.

person croX    schedule 03.02.2016