SWIFT:用键盘向上移动的UIView,在初始移动后移动非常奇怪,不会回落

Posted

技术标签:

【中文标题】SWIFT:用键盘向上移动的UIView,在初始移动后移动非常奇怪,不会回落【英文标题】:SWIFT: UIView which moves up with keyboard, moves very strangely after initial move, doesn't come back down 【发布时间】:2021-01-01 15:34:15 【问题描述】:

所以我有一个 UIView,我想在其中的文本字段正在编辑和关闭(键盘出现和隐藏)时上下移动它。这是我的键盘观察者和 UIView 的默认约束,都在 viewDidLoad 内:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIWindow.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIWindow.keyboardWillHideNotification, object: nil)
    
    let radiusViewConstraint = NSLayoutConstraint(item: searchRadiusView!, attribute: .bottom, relatedBy: .equal, toItem: super.view, attribute: .bottom, multiplier: 1.0, constant: -30.0)

以下是键盘功能:

@objc func keyboardWillShow(notification: NSNotification) 
     print("keyboardWillShow")
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue 

        self.searchRadiusView.center.y += (-1 * keyboardSize.height)
        
        view.constraints[18].constant += (-1 * keyboardSize.height)
        
     UIView.animate(withDuration: 0.5, animations: 

         self.view.layoutIfNeeded()

     )
    


   @objc func keyboardWillHide(notification: NSNotification)
     print("keyboardWillHide")
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue 

        self.searchRadiusView.center.y += 1 * keyboardSize.height
        
        view.constraints[18].constant -= (-1 * keyboardSize.height)

     UIView.animate(withDuration: 0.5, animations: 

         self.view.layoutIfNeeded()

     )
    


constraints[18] 是我在 viewDidLoad 中创建的约束。当我第一次点击文本字段时, UIView 会向上移动正确的数量。然后,当我键入我的第一个字符时,它再次向上移动相同的量,现在靠近屏幕顶部。当我关闭它时,它会回落到之前的高度(刚好高于键盘的高度,现在已关闭)。下次我编辑文本时,它会再次向上移动,但由于某种原因不是完整的 keyboard.height 量。当我解雇时,它会降低 FULL 键盘高度。然后重复此过程,直到 UIView 掉出屏幕底部。这个动作太奇怪了,我不知道问题出在哪里。我想要的只是让它随着键盘上下移动。任何想法如何解决这一问题?谢谢

【问题讨论】:

如何在文本字段即将被虚拟键盘遮挡时移动界面,这是一个已建立的解决方案很好解决的问题。我建议使用其中之一。 这就是我想要做的,我一直在研究解决方案,这个实现就是其中之一。我正试图让它为我的项目工作,如你所见,这给我带来了麻烦 显然这不是一个很好的解决方案。 :) 您对我的解决方案为何如此不稳定有任何见解吗?当我阅读它时,它似乎并没有那么糟糕,但它不起作用。我之前问过一个关于如何做这些事情的问题,在他的回答中有人告诉我我做这件事的方式是错误的,所以他给我链接了一些解决方案。我的代码是基于其中之一构建的。我已经浏览了很多,但我无法让它为我工作 可能是我。 :) 当然我最喜欢我自己的解决方案:github.com/mattneub/Programming-ios-Book-Examples/blob/xcode12/… 【参考方案1】:

你做错了太多我无法列出的事情,所以我刚刚修复了你的项目并提出了拉取请求。将拉取请求合并到您的存储库中,您会发现它现在可以正常工作了。

为了记录,以下是您做错的一些主要事情:

您在代码中向蓝色视图添加了底部约束。但是您已经 对蓝色视图设置了底部约束。因此,您现在有两个,其中一个的任何更改都会导致冲突。 Xcode 控制台非常清楚地告诉您这正在发生,但您忽略了它告诉您的内容。

您在更改约束常量的同时也更改了蓝色视图center。这可能没有造成任何伤害,但毫无意义。如果您使用约束来管理视图,则不能通过 center 来管理视图的位置;他们是对立的。

在您的显示和隐藏方法中,您检查了keyboardFrameBeginUserInfoKey。那是错误的。你想检查keyboardFrameEndUserInfoKey。问题不在于键盘现在在哪里,而是在它完成移动后将在哪里

动画是错误的。不需要 UIView 动画;您已经在动画块中。只需调用layoutIfNeeded,动画就会随着键盘的移动而发生。

您谈论和访问约束的整个方式都是错误的。您使用了不正确的表达式 super.view(您的意思可能是 self.view)。但更重要的是,您尝试通过说出self.constraints[2] 来访问所需的约束。这种东西是脆弱到极点的。正确的方法是保留对实际约束(实例属性)的引用。在这种情况下,由于约束已经存在(在故事板中),该引用可以是一个出口。

综上所述,这是我对您的代码的重写;这是所需的完整代码:

class ViewController: UIViewController 

    @IBOutlet weak var sampleTextField: UITextField!
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
    var originalConstant: CGFloat = 0
    
    override func viewDidLoad() 
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIWindow.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIWindow.keyboardWillHideNotification, object: nil)
        self.originalConstant = bottomConstraint.constant
    

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
        sampleTextField.endEditing(true)
    


extension ViewController 
    @objc func keyboardWillShow(notification: NSNotification) 
        print("keyboardWillShow")
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue 
            self.bottomConstraint.constant += keyboardSize.height + 5
            self.view.layoutIfNeeded()
        
    

    @objc func keyboardWillHide(notification: NSNotification)
        print("keyboardWillHide")
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue 
            self.bottomConstraint.constant = self.originalConstant
            self.view.layoutIfNeeded()
        
    

说了这么多,代码仍然是错误的,因为你没有考虑到当键盘已经出现时你会收到keyboardWillShow 通知的非常现实的可能性显示。不过,我把它留给你以后调查。

【讨论】:

好吧,非常感谢您付出的时间和精力。我很感激帮助。如您所见,我对约束和视图很陌生哈哈。我知道我在故事板和代码中设置了一个约束,虽然我不知道我可以制作一个约束出口,但这更有意义。我想我应该意识到这是一个问题。至于您的最后一条语句,“keyboardWillShow”每次都会在开始时调用一次,并且在您第一次键入第一个字符时调用一次。如果它在其他点上调用它,我会弄清楚的。再次,非常感谢 我们可以做到这一点的一种方法是不使用+=。相反,只需将self.bottomConstraint.constant 设置为实际数字。这样一来,它总是恰到好处。 啊,是的,这也更有意义。我在堆栈溢出时发现的其他一些代码之后对此进行了建模,但我应该意识到要改变它。再次感谢! 不过,我认为我在链接中指出的代码是一种更好的方法。我们在正确的时间滑动整个界面,适量。 你说“整个界面”是什么意思?只移动需要移动的东西是不是很糟糕?

以上是关于SWIFT:用键盘向上移动的UIView,在初始移动后移动非常奇怪,不会回落的主要内容,如果未能解决你的问题,请参考以下文章

仅当键盘阻止 UITextField / UITextView 时才向上移动 UIView

出现键盘时向上移动 UIView

使用键盘下的UITextFields向上移动UIView

Swift 防止 TabBar 在键盘处于活动状态时向上移动

当键盘出现在 Swift 中时 UITextField 向上移动

当使用 Swift 出现键盘时如何向上移动 UIViewController 的内容