keyboardWillShow被其他应用程序的键盘调用

我知道这是应该发生的事情,但是这导致了我不知如何解决的问题。

我想在键盘显示时将视图向上移动,以便我的文本字段保持可见。

我的文本字段有数字小键盘。

我使用通知和keyboardWillShow/Hide来移动我的视图向上/向下select文本字段时。

现在假设我点击一个文本字段,然后切换到另一个使用不同的键盘(不是数字键盘)的应用程序。 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 }) } } 

编辑:由于@JasonNam在他的答案中指出,viewWillDisappear不会在切换应用程序时被调用,所以我不得不添加一个applicationWillResignActive通知来删除键盘通知和一个applicationDidBecomeActive通知将它们添加回来。


编辑2:@ sahara108的解决scheme似乎更清洁,我看不出任何缺点。 我只需要在keyboardWillShow中做任何事情之前检查UIApplication.sharedApplication().applicationState == .Active

我build议你检查一下你的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() } } 

更新:而不是检查firstResponder,如果您检查UIApplication.shareApplication().applicationSate == .Active

仅适用于iOS 9+:

来自键盘的NSNotification包含以下内容:

UIKeyboardIsLocalUserInfoKey – 包含布尔值的NSNumber对象的键,标识键盘是否属于当前应用程序。

在我的情况下,我也这样做(这也可能需要为OP):

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

这样键盘在应用程序之间切换时不会隐藏。

你认为几乎是正确的:你必须删除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) } 

您可以注销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) } 

你可以在keyboardWillShow做任何事情之前,确保视图包含第一个响应者。 使用像这样的UIView扩展(或类别),不能find快速的等价物,但是你得到了这个想法,你可以检测任何视图的子视图是否是第一响应者。 我相信这应该也适用于在iOS 9上同时在前台有两个应用程序的情况。

只需检查应用程序状态是否处于活动状态即可:

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