其他应用程序的键盘调用keyboardWillShow
Posted
技术标签:
【中文标题】其他应用程序的键盘调用keyboardWillShow【英文标题】:keyboardWillShow gets called for other app's keyboards 【发布时间】:2015-12-22 06:16:00 【问题描述】:我知道这是应该发生的,但它给我带来了我不知道如何解决的问题。
我想在键盘显示时向上移动视图,以便我的文本字段保持可见。
我的文本字段有数字键盘。
我使用通知和keyboardWillShow/Hide
在选择文本字段时向上/向下移动我的视图。
现在假设我点击一个文本字段,然后切换到另一个使用不同键盘(不是数字键盘)的应用程序。 keyboardWillShow
使用错误键盘的大小(来自另一个应用程序的键盘)调用,并且我的视图移动了错误的数量(它甚至根本不应该移动)。因此,当我回到我的应用程序时,我的视图位于错误的位置,甚至没有显示键盘,然后调用 keyboardWillHide
并将视图移回原位(不知从何而来)。但是一开始就不应该为其他应用调用keyboardWillShow
。
我正在删除viewWillDisappear
上的通知,但这种情况仍然会发生……也许在为我调用viewWillDisappear
之前,其他应用程序调用了keyboardWillShow
?
这是我的代码:
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
。
【问题讨论】:
代码中的一个大问题。您将self
添加为通知观察者,但您尝试删除self.view
。您需要删除self
。
糟糕,我最初是删除self
,但我注意到了这个问题并开始尝试各种疯狂的事情,包括从self.view
而不是self
中删除观察者,然后我忘记了当我在这里发布代码时,将那部分改回来。不过,问题仍然存在。环顾四周,我发现这显然应该发生......但我不希望它发生,我不知道该怎么做。
【参考方案1】:
我建议您检查您的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
更安全
【讨论】:
它不起作用。检查我对艾哈迈德回答的评论。 :// 哦,我很惊讶它不起作用。所以请检查应用程序状态。请参阅我的更新答案。【参考方案2】:仅限 iOS 9+:
来自键盘的 NSNotification 包含以下内容:
UIKeyboardIsLocalUserInfoKey - NSNumber 对象的键,其中包含一个标识键盘是否属于当前应用的布尔值。
在我的情况下,我也这样做(这可能也是 OP 所需要的):
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool
return UIApplication.shared.applicationState == .active
这样在应用程序之间切换时键盘不会隐藏。
【讨论】:
【参考方案3】:只需检查应用状态是否处于活动状态即可:
- (void)handleKeyboardWillShowNotification:(NSNotification *)notifaction
if([UIApplication sharedApplication].applicationState != UIApplicationStateActive)
return;
//your code below...
【讨论】:
【参考方案4】:您的想法几乎是对的:您必须删除 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)
【讨论】:
这不是真的。notificationCenter.removeObserver(self)
将从所有已注册的通知中删除 self
。 OP 代码的问题在于他们试图删除 self.view
而不是 self
。
是的,可能有效,但不是最佳实践:在 dealloc 方法中这样做是安全的,但不应该使用其他方式——使用 removeObserver:name:object: 代替。 link
@jorn 刚试过,没解决我的问题。 ://【参考方案5】:
您可以在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)
【讨论】:
嘿,你能再检查一下我的答案吗?实际上切换应用时不会调用 viewDidDisappear :( 这正是我所做的,它没有改变任何事情。我的视图仍然根据其他应用的键盘移动。 你说得对,不过,我尝试在 viewDidDisappear 打印一些东西,但在切换应用程序时它不会被调用。但是,ApplicationDidEnterBackground 被调用太晚了,只有在另一个应用程序调用了 keyboardWillAppear 并且我的视图被移动之后。 啊哈!applicationWillResignActive
被及时调用,然后一切正常!
哦,听起来不错!祝你的编码好运:)【参考方案6】:
在keyboardWillShow
中执行任何操作之前,您可以确保视图包含第一响应者。使用像 this one 这样的 UIView 扩展(或类别) - 抱歉找不到 swift 等价物,但你明白了 - 你可以检测视图的任何子视图是否是第一响应者。我相信这也应该适用于在 ios 9 上同时在前台有 2 个应用程序的情况。
【讨论】:
那将是一个更清洁的解决方案,但我刚刚尝试过,但它不起作用。这很奇怪,但显然keyboardWillAppear
在所选文本字段退出第一响应者之前被另一个应用程序调用。如果我尝试打印textField.isFirstResponder()
,然后是视图的偏移量,然后是textField.isFirstResponder()
,我会很快得到true
,偏移量和false
。【参考方案7】:
我无法使用应用程序生命周期方法,但我能够通过检查当前视图中的任何文本字段是否为 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)
【讨论】:
使用这个答案的方法,没有代码,没有代码返工,它可能最好作为评论而不是答案。 更新了!感谢您的反馈以上是关于其他应用程序的键盘调用keyboardWillShow的主要内容,如果未能解决你的问题,请参考以下文章
想用VC++做个全局键盘改键,键盘钩子代码,详细怎么做。跪求代码。