我知道这是应该发生的事情,但它导致了我不知道如何修复的问题。
我想在键盘显示时向上移动我的视图,以便我的文本字段保持可见。
我的文本字段有数字键盘。
当选择文本字段时,我使用通知和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
。
我建议你检查你的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()
}
}
更新:如果你检查qazxsw poi,它会更安全,而不是检查第一个响应者
仅限iOS 9+:
来自键盘的NSNotification包含以下内容:
UIKeyboardIsLocalUserInfoKey - 包含布尔值的NSNumber对象的键,该布尔值标识键盘是否属于当前应用程序。
在我的情况下,我也这样做(OP也可能需要):
UIApplication.shareApplication().applicationSate == .Active
这样,在应用程序之间切换时键盘不会隐藏。
只需检查应用程序状态是否处于活动状态即可:
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
return UIApplication.shared.applicationState == .active
}
您认为几乎是正确的:您必须删除viewWillDisappear中的特定通知:
- (void)handleKeyboardWillShowNotification:(NSNotification *)notifaction{
if([UIApplication sharedApplication].applicationState != UIApplicationStateActive){
return;
}
//your code below...
}
您可以在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
-sorry这样的UIView扩展(或类别)无法找到快速等效但你明白了 - 你可以检测到任何视图的子视图是否是第一响应者。我相信这也适用于在iOS 9上同时在前台拥有2个应用程序的情况。