【问题标题】:UITextField text jumps iOS 9UITextField 文字跳转 iOS 9
【发布时间】:2016-01-04 09:05:42
【问题描述】:

上一个问题的链接: UITextField text jumps

简单地说: 我有 ViewController 和 2 个 UITextField 元素。当loginField为firstResponder时,后

self.passwordField.becomeFirstResponder()

登录字段中的文本跳转到左上角并返回。更奇怪的是:此故障仅在第一次重现,然后您需要重新创建 ViewController 以观察此行为

这是故障视频http://tinypic.com/player.php?v=6nsemw%3E&s=8#.VgVb3cuqpHx

我最终得到了这个(不适用于 iOS 9):

func textFieldShouldReturn(textField: UITextField) -> Bool {
    if textField === self.loginField {
        self.loginField.resignFirstResponder()
        // Shitty workaround. Hi, Apple!
        self.loginField.setNeedsLayout()
        self.loginField.layoutIfNeeded()

        self.passwordField.becomeFirstResponder()
        return false
    }

    return true
}

有没有人被这个错误困住了?有什么建议吗?

键盘通知处理程序

我的主视图是 UIScrollView,为此我将底部空间更改为超级视图,因此即使显示键盘,用户也可以滚动所有内容

func keyboardWillShow(notification : NSNotification) {
    let keyboardInfo = notification.userInfo!
    let keyboardFrame = keyboardInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue
    let animDuration = keyboardInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue!

    UIView.animateWithDuration(animDuration, animations: {
        self.scrollViewBottom.constant = keyboardFrame.height
        self.view.layoutIfNeeded()

        let offsetY = CGRectGetMaxY(self.loginButton.frame) + 10 - self.scrollView.frame.height
        if offsetY > 0 {
            self.scrollView.contentOffset = CGPointMake(0, offsetY)
        }
    })
}

func keyboardWillHide(notification : NSNotification) {
    self.scrollViewBottom.constant = 0
    self.view.layoutIfNeeded()
}

我在 iOS7 中发现键盘通知,8 和 9 非常不同。因此,在 iOS 9 中,即使键盘不会显示/隐藏,也会在更改 firstResponder 时发送通知。此外,当我通过点击 textField 更改 firstResponder (不点击由我的代码处理的键盘上的 Next)时,只有 KeyboardWillShow 通知,没有 KeyboardWillHide。至于我,userInfo 有一些垃圾帧值,这里是使用下一步按钮更改第一响应者时的日志(工作正常,没有故障):

2015-10-07 12:54:13.870 键盘将隐藏: [UIKeyboardFrameBeginUserInfoKey: NSRect: {{0, 352}, {320, 216}}, UIKeyboardCenterBeginUserInfoKey: NSPoint: {160, 460}, UIKeyboardFrameEndUserInfoKey: NSRect: {{0, 568}, {320, 216}}, UIKeyboardCenterEndUserInfoKey: NSPoint: {160, 676}, UIKeyboardAnimationDurationUserInfoKey: 0.25, UIKeyboardIsLocalUserInfoKey: 1, UIKeyboardBoundsUserInfoKey: NSRect: {{0, 0}, {320, 216}},UIKeyboardAnimationCurveUserInfoKey: 7] 2015-10-07 12:54:13.896 键盘将显示: [UIKeyboardFrameBeginUserInfoKey: NSRect: {{0, 352}, {320, 216}}, UIKeyboardCenterBeginUserInfoKey: NSPoint: {160, 460}, UIKeyboardFrameEndUserInfoKey: NSRect: {{0, 352}, {320, 216}}, UIKeyboardCenterEndUserInfoKey: NSPoint: {160, 460}, UIKeyboardAnimationDurationUserInfoKey: 0.25, UIKeyboardIsLocalUserInfoKey: 1, UIKeyboardBoundsUserInfoKey: NSRect: {{0, 0}, {320, 216}},UIKeyboardAnimationCurveUserInfoKey: 7]

当我点击第二个文本字段时,这里是日志:

2015-10-07 12:55:13.879 keyboardWillShow:[UIKeyboardFrameBeginUserInfoKey: NSRect: {{0, 352}, {320, 216}},UIKeyboardCenterBeginUserInfoKey:NSPoint:{160, 460}, UIKeyboardFrameEndUserInfoKey: NSRect: {{0, 352}, {320, 216}}, UIKeyboardCenterEndUserInfoKey: NSPoint: {160, 460},
UIKeyboardAnimationDurationUserInfoKey: 0.25, UIKeyboardIsLocalUserInfoKey: 1, UIKeyboardBoundsUserInfoKey: NSRect: {{0, 0}, {320, 216}},UIKeyboardAnimationCurveUserInfoKey: 7]

分辨率

我发现我有另一个键盘控制器,它接收键盘通知并制作一些动画。这就是问题所在

【问题讨论】:

  • 你是如何处理键盘的?您是否已注册键盘通知会出现/隐藏?如果是,您可以发布代码吗?
  • Tnx 寻求帮助,检查已编辑的帖子
  • 你得到 will hide 通知,因为你在 becomeFirstResponder() 之前调用了 resignFirstResponder(),“跳跃”是由 keyboardWillShow() 中的动画引起的
  • 2017 年... Timur 下面的解决方案似乎效果很好。

标签: ios iphone swift swift2 ios9


【解决方案1】:

根据您编辑的问题,当您点击键盘上的下一步按钮时,我可以看到这一点:

  1. 您拨打的是resignFirstResponder(),然后是becomeFirstResponder()。这会调用keyboardWillHide 通知,然后调用keyboardWillShow 通知
  2. keyboardWillHide 中有self.view.layoutIfNeeded(),它在没有动画的情况下布局视图(和子视图 - 文本字段)。

因此,文本字段布局是“固定的”,当您在 keyboardWillShow 中进行动画处理时,文本字段中的文本不再“跳跃”,因为您在 keyboardWillHide 中进行了布局。

但是当您只是点击另一个文本字段时,只会调用 keyboardWillShow,文本字段中的布局不是“固定”的,当您为视图设置动画时,文本会执行“跳转”动画。

这就是为什么当你在键盘上点击 next 时它不会跳转,但当你只是点击另一个文本字段时它会跳转。

所以我建议把它改成这样:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    if textField === self.loginField {
        self.passwordField.becomeFirstResponder()
        return false
    }

    return true
}

func keyboardWillShow(notification : NSNotification) {
    let keyboardInfo = notification.userInfo!
    let keyboardFrame = keyboardInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue
    let animDuration = keyboardInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue!

    self.loginField.layoutIfNeeded()
    self.passwordField.layoutIfNeeded()

    if keyboardFrame.height != self.scrollViewBottom.constant {
        UIView.animateWithDuration(animDuration, animations: {
            self.scrollViewBottom.constant = keyboardFrame.height
            self.view.layoutIfNeeded()

            let offsetY = CGRectGetMaxY(self.loginButton.frame) + 10 - self.scrollView.frame.height
            if offsetY > 0 {
                self.scrollView.contentOffset = CGPointMake(0, offsetY)
            }
        })
    }
}

【讨论】:

  • 很遗憾没有帮助
  • 我什至评论了我的键盘通知处理程序 - 故障仍然存在
  • 这样包装 UIView.performWithoutAnimation({ self.loginField.layoutIfNeeded() self.passwordField.layoutIfNeeded() }) 但它不起作用
【解决方案2】:

转至haluzak:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    if textField === self.loginField {
        self.passwordField.becomeFirstResponder()
        return false
    }

    return true
}

func keyboardWillShow(notification : NSNotification) {
    let keyboardInfo = notification.userInfo!
    let keyboardFrame = keyboardInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue
    let animDuration = keyboardInfo[UIKeyboardAnimationDurationUserInfoKey]!.doubleValue!

    UIView.performWithoutAnimation({
        self.loginField.layoutIfNeeded()
        self.passwordField.layoutIfNeeded()
    })

    if keyboardFrame.height != self.scrollViewBottom.constant {
        UIView.animateWithDuration(animDuration, animations: {
            self.scrollViewBottom.constant = keyboardFrame.height
            self.view.layoutIfNeeded()

            let offsetY = CGRectGetMaxY(self.loginButton.frame) + 10 - self.scrollView.frame.height
            if offsetY > 0 {
                self.scrollView.contentOffset = CGPointMake(0, offsetY)
            }
        })
    }
}

【讨论】:

  • performWithoutAnimation 部分对我来说很关键。
【解决方案3】:

我在尝试更换第一响应者时遇到了同样的问题。这个问题有更好的解决方案。只需实现 UITextField 子类并在可以为文本字段设置动画的地方使用(键盘、第一响应切换等)。

@interface UITextFieldFixed : UITextField

@end

@implementation UITextFieldFixed

- (BOOL)resignFirstResponder
{
    BOOL result = [super resignFirstResponder];
    [self setNeedsLayout];
    [self layoutIfNeeded];
    return result;
}

@end

关于问题的更多信息:https://github.com/foundry/UITextFieldBug

为 Swift3 复制和粘贴...

class UITextFieldFixed: UITextField {
    override func resignFirstResponder() -> Bool {
        let r = super.resignFirstResponder()
        self.setNeedsLayout()
        self.layoutIfNeeded()
        return r
    }
}

【讨论】:

  • 这太棒了 Timur,谢谢。我在 Swift3 中粘贴了相同的内容 - 随意编辑或撤消,Timur!
  • 在这里发送赏金以获得很好的答案
  • 效果很好,好像你也不需要setNeedsLayout
【解决方案4】:

在你做任何动画之前,把这段代码放在你的键盘上会显示通知:

UIView.performWithoutAnimation({
    self.yourTextField1.layoutIfNeeded()
    self.yourTextField2.layoutIfNeeded()
    //etc.
})

【讨论】:

    【解决方案5】:

    这是您需要编写的用于停止文本跳转的简单代码。这对我有用。

    func textFieldDidEndEditing(_ textField: UITextField) { // Workaround for the jumping text bug. textField.resignFirstResponder() textField.layoutIfNeeded() }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-12
      • 1970-01-01
      • 2017-08-23
      • 1970-01-01
      • 1970-01-01
      • 2015-08-14
      • 2017-11-13
      • 1970-01-01
      相关资源
      最近更新 更多