2015-12-22 8 views
4

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

Я хочу переместить мой взгляд вверх, когда клавиатура покажет, так что мои текстовые поля остаются видимыми.

В моих текстовых полях есть цифровые клавиатуры.

Я использую уведомления и keyboardWillShow/Hide, чтобы переместить мой просмотр вверх/вниз, когда выбрано текстовое поле.

Теперь предположим, что я нажимаю на текстовое поле, а затем переключаюсь на другое приложение, использующее другую клавиатуру (а не цифровую клавиатуру). keyboardWillShow вызывается с размером неправильной клавиатуры (другой из другого приложения), и мое представление перемещено неправильно (оно вообще не должно двигаться). Поэтому, когда я возвращаюсь к своему приложению, мое мнение не в том месте, и клавиатура даже не отображается, а затем вызывается keyboardWillHide, и представление возвращается на место (из ниоткуда). Но keyboardWillShow не следует даже вызывать для другого приложения.

Я удаляю уведомления на viewWillDisappear, но это все еще происходит ... может быть, keyboardWillShow вызывается для других приложений до того, как viewWillDisappear вызывается для моего?

Вот мой код:

override func viewWillAppear(animated: Bool) { 
    super.viewWillAppear(animated) 
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil) 
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil) 
    for subview in self.view.subviews { 
     if subview.isKindOfClass(UITextField) { 
      let textField = subview as! UITextField 
      textField.addTarget(self, action: "textFieldDidReturn:", forControlEvents: UIControlEvents.EditingDidEndOnExit) 
      textField.addTarget(self, action: "textFieldDidBeginEditing:", forControlEvents: UIControlEvents.EditingDidBegin) 
     } 
    } 
} 

override func viewWillDisappear(animated: Bool) { 
    super.viewWillDisappear(animated) 
    NSNotificationCenter.defaultCenter().removeObserver(self) 
} 

func keyboardWillShow(notification: NSNotification) { 
    self.keyboardIsShowing = true 
    if let info = notification.userInfo { 
     self.keyboardFrame = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue() 
     self.arrangeViewOffsetFromKeyboard() 
    } 
} 

func keyboardWillHide(notification: NSNotification) { 
    self.keyboardIsShowing = false 
    self.returnViewToInitialFrame() 
} 

func arrangeViewOffsetFromKeyboard() { 
    if let textField = activeTextField { 
     let theApp: UIApplication = UIApplication.sharedApplication() 
     let windowView: UIView? = theApp.delegate!.window! 
     let textFieldLowerPoint = CGPoint(x: textField.frame.origin.x, y: textField.frame.origin.y + textField.frame.size.height) 
     let convertedTextFieldLowerPoint = textField.superview!.convertPoint(textFieldLowerPoint, toView: windowView) 
     let targetTextFieldLowerPoint = CGPoint(x: textField.frame.origin.x, y: self.keyboardFrame.origin.y) 
     let targetPointOffset = targetTextFieldLowerPoint.y - convertedTextFieldLowerPoint.y 
     let adjustedViewFrameCenter = CGPoint(x: self.view.center.x, y: self.view.center.y + targetPointOffset) 
     print(targetPointOffset) // When I change to a different app this prints the wrong value… but none of this should even get called. 
     if targetPointOffset < 0 { 
      UIView.animateWithDuration(0.3, animations: { 
       self.view.center = adjustedViewFrameCenter 
      }) 
     } 
    } 
} 

func returnViewToInitialFrame() { 
    let initialViewRect = CGRect(x: 0.0, y: 0.0, width: self.view.frame.size.width, height: self.view.frame.size.height) 
    if !CGRectEqualToRect(initialViewRect, self.view.frame) { 
     UIView.animateWithDuration(0.2, animations: { 
      self.view.frame = initialViewRect 
     }) 
    } 
} 

Edit: Как @JasonNam отметил в своем ответе, viewWillDisappear не дозвонились при переключении приложений, так что мне пришлось добавить applicationWillResignActive уведомление, чтобы удалить уведомления о клавиатуре и уведомление applicationDidBecomeActive, чтобы добавить их обратно.


Редактировать 2: Решение @ sahara108 выглядит чище, и я не вижу недостатков. Мне просто нужно было проверить UIApplication.sharedApplication().applicationState == .Active, прежде чем делать что-либо в клавиатуреWillShow.

+0

Одна большая проблема в вашем коде. Вы добавляете 'self' в качестве наблюдателя уведомлений, но пытаетесь удалить' self.view'. Вам нужно удалить 'self'. – rmaddy

+0

К сожалению, я изначально удалял 'self', но я заметил эту проблему и начал пробовать всевозможные сумасшедшие вещи, включая удаление наблюдателя из' self.view' вместо 'self', после чего я забыл изменить эту часть, когда Я разместил здесь код. Однако проблема остается в любом случае. И, озираясь, я узнал, что, по-видимому, это должно произойти ... но я не хочу этого, и я не знаю, что делать. – dbmrq

ответ

4

Я предлагаю вам проверить, если ваш textField является первым ответчиком в keyboardWillShown методе. Если это не так, просто игнорируйте уведомление.

func keyboardWillShow(notification: NSNotification) { 
    if !myTextField.isFirstResponder() { 
     return 
    } 
    self.keyboardIsShowing = true 
    if let info = notification.userInfo { 
     self.keyboardFrame = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue() 
     self.arrangeViewOffsetFromKeyboard() 
    } 
} 

UPDATE: Вместо проверки для firstResponder, это безопаснее, если вы проверяете UIApplication.shareApplication().applicationSate == .Active

+0

Это не работает. Проверьте мой комментарий к ответу Ахмеда. :/ – dbmrq

+0

О, я удивлен, что это не сработает. Поэтому проверьте состояние приложения. См. Мой обновленный ответ. – sahara108

+1

Ах! Это работало как шарм. Спасибо! – dbmrq

0

Вы думали, что почти правильное право: Вы должны удалить определенные уведомления в viewWillDisappear:

override func viewWillDisappear(animated: Bool) { 
    super.viewWillDisappear(animated) 

    let notificationCenter = NSNotificationCenter.defaultCenter() 
    notificationCenter.removeObserver(self, name: UIKeyboardWillShowNotification, object: nil) 
    notificationCenter.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil) 
} 
+0

Это неправда. 'notificationCenter.removeObserver (self)' будет удалять 'self' из всех зарегистрированных уведомлений. Проблема с кодом OP заключается в том, что они пытаются удалить 'self.view' вместо' self'. – rmaddy

+0

yep, может работать, но это не самая лучшая практика: _Это безопасно делать в методе dealloc, но в противном случае использовать не следует - используйте removeObserver: name: object: вместо ._ [link] (https: // разработчик .apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/# // apple_ref/occ/instm/NSNotificationCenter/removeObserver :) –

+0

@jorn Просто попробовал, что это не решило мою проблему. :/ – dbmrq

0

Вы можете отменить регистрацию уведомления о UIApplicationDidEnterBackgroundNotification и снова зарегистрироваться в UIApplicationDidBecomeActiveNotification. Я не уверен, что такое поведение преднамеренно, но определенно что-то неожиданное для меня тоже.

override func viewDidLoad() { 
    super.viewDidLoad() 

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationBecomeActive", name: UIApplicationDidBecomeActiveNotification, object: nil) 
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationDidEnterBackground", name: UIApplicationDidEnterBackgroundNotification, object: nil) 
} 

func applicationBecomeActive() 
{ 
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil) 
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil) 
} 

func applicationDidEnterBackground() 
{ 
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil) 
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil) 
} 
+0

Просто попробовал это, это не сработало. :/ – dbmrq

+0

Эй, ты снова проверишь мой ответ. Фактически viewDidDisappear не вызывается при переключении приложений :( –

+0

Это именно то, что я сделал, это ничего не меняет. Мое мнение по-прежнему перемещается в соответствии с клавиатурой другого приложения. – dbmrq

0

Вы можете убедиться, что представление содержит первый ответчик, прежде чем делать какие-либо вещи в keyboardWillShow. Использование расширения UIView (или категории), такого как this one, не могло найти быстрый эквивалент, но вы получили идею - вы можете определить, является ли какое-либо из подзонов представления первым ответчиком.Я считаю, что это должно работать в ситуациях, как с 2 приложением на переднем плане, в то же время на прошивке 9.

+0

Это было бы более чистым решением, но я просто попробовал, и он не работает. Это очень странно, но, видимо, 'keyboardWillAppear' вызывается для другого приложения, прежде чем выбранное текстовое поле уйдет в отставку с первого ответчика. Если я попытаюсь напечатать 'textField.isFirstResponder()', тогда смещение представления, а затем 'textField.isFirstResponder()' снова, я получаю 'true', смещение и' false' один за другим очень быстро. – dbmrq

4

IOS 9+ только:

NSNotification, который поставляется с клавиатуры содержит следующее:

UIKeyboardIsLocalUserInfoKey - Ключ для объекта NSNumber, содержащий логическое значение, определяющее, принадлежит ли клавиатура текущему приложению.

В моем случае я также сделать это (что, вероятно, необходимо для ОП тоже):

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { 
    return UIApplication.shared.applicationState == .active 
} 

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

1

Просто проверьте, активен ли будет хорошо состояние приложения:

- (void)handleKeyboardWillShowNotification:(NSNotification *)notifaction{ 
    if([UIApplication sharedApplication].applicationState != UIApplicationStateActive){ 
     return; 
    } 
    //your code below... 
} 
+0

Все еще работает для меня. Спасибо тебе за это. – ChrisOSX

Смежные вопросы