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

时间:2015-12-22 06:16:00

标签: ios swift nsnotificationcenter

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

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

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

我选择文本字段时使用通知和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
        })
    }
}

编辑:正如@JasonNam在他的回答中指出的那样,切换应用时不会调用viewWillDisappear,所以我不得不添加applicationWillResignActive通知以删除键盘通知和applicationDidBecomeActive通知把它们加回来。

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

7 个答案:

答案 0 :(得分:9)

我建议您使用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()
    }
}

<强>更新 如果您选中UIApplication.shareApplication().applicationSate == .Active

,则不会检查firstResponder,而是更安全

答案 1 :(得分:4)

仅限iOS 9+:

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

  

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

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

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

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

答案 2 :(得分:1)

只需检查应用状态是否有效就可以了:

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

答案 3 :(得分: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)
}

答案 4 :(得分: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)
}

答案 5 :(得分:0)

keyboardWillShow中执行任何操作之前,您可以确保视图包含第一响应者。使用像this one -sorry这样的UIView扩展(或类别)无法找到快速等效但你明白了 - 你可以检测到任何视图的子视图是否是第一响应者。我相信这也适用于在iOS 9上同时在前台安装2个应用程序的情况。

答案 6 :(得分:0)

我无法使用“应用程序生命周期”方法,但可以通过检查当前视图中的任何文本字段是否为firstResponder来为自己解决此问题。

已更新代码:

NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification,object: nil)

@objc func keyboardWillShow(_ notification: Notification) {
    if storeNameTextField.isFirstResponder || methodTextField.isFirstResponder || amountTextField.isFirstResponder || dateTextField.isFirstResponder || categoryTextField.isFirstResponder {
        if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            let keyboardRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keyboardRectangle.height
            self.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight - 22, right: 0)
        }
    }
}
相关问题