UIKeyboardDidShowNotification вызывается несколько раз, а иногда и с неверными размерами клавиатуры
Я пытаюсь переместить UITextView над клавиатурой всякий раз, когда клавиатура появляется / меняется. Допустим, у меня отображается английская клавиатура, а затем я переключаюсь непосредственно на китайскую клавиатуру (которая выше стандартной английской клавиатуры). В этом сценарии мое текстовое представление всегда кажется слишком высоким (невооруженным глазом это выглядит, как будто текстовое представление неправильно смещено на размер китайской клавиатуры "вспомогательное представление ввода". Я отправил бы изображение, но не хватило бы репутации, чтобы сделать так:)). Я корректирую позицию просмотра текста, когда мое приложение получает UIKeyboardDidShowNotification (используя UIKeyboardFrameEndUserInfoKey для получения высоты), и после некоторого исследования UIKeyboardDidShowNotification вызывается несколько раз, часто с неправильными размерами клавиатуры (я NSLoged словарь userInfo). Я регистрируюсь для своих уведомлений клавиатуры в ViewWillAppear и отменяю регистрацию в ViewWillDisappear. Я не могу определить, что может вызывать срабатывание этого уведомления несколько раз; Насколько я понимаю, это уведомление должно быть запущено только один раз, сразу после завершения отображения клавиатуры. Дополнительное примечание: я NSLogged 'self' в методе, который отвечает на UIKeyboardDidShowNotification, и это на самом деле всегда один и тот же объект View Controller.
Однако даже при многократном срабатывании этого уведомления я все еще не понимаю, почему высота клавиатуры будет отличаться для некоторых из этих уведомлений. Одно из уведомлений всегда имеет правильную высоту, но если это не последнее уведомление, запущенное текстовое представление оказывается в неправильном месте. Любое понимание о том, как дальнейшее устранение неполадок будет высоко ценится!
РЕДАКТИРОВАТЬ: Чем больше я тестирую, тем больше, кажется, проблема именно с китайской клавиатурой. Всякий раз, когда я переключаю клавиатуру с английского на китайский, я получаю три UIKeyboardWillShowNotifications:
2014-12-24 22:49:29.385 Example[1055:421943] info dictionary: {
UIKeyboardAnimationCurveUserInfoKey = 0;
UIKeyboardAnimationDurationUserInfoKey = 0;
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 252}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 460}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 442}";
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 352}, {320, 216}}";
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 316}, {320, 252}}";
}
2014-12-24 22:49:29.408 Example[1055:421943] info dictionary: {
UIKeyboardAnimationCurveUserInfoKey = 0;
UIKeyboardAnimationDurationUserInfoKey = 0;
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 442}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 460}";
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 316}, {320, 252}}";
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 352}, {320, 216}}";
}
2014-12-24 22:49:29.420 Example[1055:421943] info dictionary: {
UIKeyboardAnimationCurveUserInfoKey = 0;
UIKeyboardAnimationDurationUserInfoKey = 0;
UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 288}}";
UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 442}";
UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 424}";
UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 316}, {320, 252}}";
UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 280}, {320, 288}}";
}
Первый имеет правильную высоту конца: 252. Однако следующие два неверны в 216 и 288. Это происходит надежно.
Вот несколько фрагментов, демонстрирующих, как я управляю подписками на уведомления:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self registerForKeyboardNotifications];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardDidShowNotification
object:nil];
}
- (void)registerForKeyboardNotifications {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidHide:)
name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification object:nil];
}
5 ответов
Если вы используете комбинацию клавиш Cmd+K на симуляторе, чтобы показать / скрыть клавиатуру, она может вызываться несколько раз, так как это не оставляет текстовое поле первым респондентом.
Если вы вместо этого используете клавиатуру Return
Кнопка затем он будет делать правильные вещи и уйти в отставку.
Основная причина заключается в том, что ваше уведомление вызывается несколько раз. Например, в swift, если у вас есть метод NSNotificationCenter.defaultCenter(). AddObserver(xx"keyboardWillShow"xx) внутри метода viewWillAppear, и если вы переходите назад и вперед между представлениями, то это приведет к тому, что несколько наблюдателей одного и того же " keyboardWillShow". Вместо этого вам следует рассмотреть возможность перемещения вызова addObserver в метод "viewDidLoad()". В качестве альтернативы вы можете зарегистрировать / отменить регистрацию наблюдателя, когда вид появляется / исчезает.
- (void)keyboardDidAppear:(NSNotification *)note {
CGSize keyboardSize = [note.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0);
textView.contentInset = contentInsets;
textView.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillBeHidden:(NSNotification *)note {
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
textView.contentInset = contentInsets;
textView.scrollIndicatorInsets = contentInsets;
}
Решение от официального решения Apple здесь, как сказал 曹 сказал.
В моем случае я регистрируюсь для своих уведомлений клавиатуры в ViewWillAppear и отменяю регистрацию в ViewWillDisappear тоже. Но это приведет к многократному запуску обработчика UIKeyboardDidShowNotification. Похоже, что метод отмены регистрации не работает, поэтому каждый раз, когда появляется представление, счетчики Observer для UIKeyboardDidShowNotification плюс 1. Затем вы касаетесь внутри UITextField, уведомляете несколько Observer, снова и снова запускается обработчик.
Таким образом, вы должны зарегистрироваться для уведомлений клавиатуры в ViewDidLoad и не отменять регистрацию. Так же, как Apple, упомянутые на этой странице,
// Вызвать этот метод где-нибудь в вашем коде установки контроллера представления.
Я думаю, что "настройка" означает ViewDidLoad. И в списке "Обработка кода уведомлений клавиатуры" нет ViewWillDisappear.
Вот мой обработчик для уведомления клавиатуры, и это работает.
func keyboardWillShow(notification: NSNotification) {
let userInfo = notification.userInfo!
let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
debugPrint("Before",NotifyView.frame)
NotifyView.frame.offsetInPlace(dx: 0, dy: -keyboardHeight)
debugPrint("After",NotifyView.frame)
}
func keyboardWillHide(notification: NSNotification) {//Do Nothing
}
Я заметил, что это иногда вызывается дважды, когда вы долгое время нажимаете на
Чтобы избежать проблем с анимацией пользовательского интерфейса на основе высоты клавиатуры, я использовал ограничения, поэтому даже если блок анимации вызывается несколько раз, он все равно будет создавать допустимую анимацию. Нам просто нужно вызвать следующий метод на клавиатуре, который покажет и скроет методы обработчика, чтобы получить правильную анимацию:
func animateWithKeyboard(for notification: NSNotification, animation: ((CGRect) -> Void)?) {
guard let frame = notification.keyboardEndFrame,
let duration = notification.keyboardAnimationDuration,
let curve = notification.keyboardAnimationCurve else {
return
}
UIViewPropertyAnimator(duration: duration, curve: curve) {
animation?(frame)
self.view?.layoutIfNeeded()
}.startAnimation()
}
Вот также расширение
extension NSNotification {
var keyboardAnimationDuration: Double? {
userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double
}
var keyboardAnimationCurve: UIView.AnimationCurve? {
guard let curveValue = userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int else { return nil }
return UIView.AnimationCurve(rawValue: curveValue)
}
var keyboardEndFrame: CGRect? {
(userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
}
}