Reputation:
So I have a UIView which I want to move up and down as the textfield within it is editing and dismissing (keyboard appears and hides). Here is my keyboard observers, and the UIView's default constraint, all inside the 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)
Here are the keyboard functions:
@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] is the constraint I created in my viewDidLoad. When I first tap the textfield, the UIView moves up the correct amount. Then, when I type my first character, it moves up that same amount again, and is now near the top of the screen. When I dismiss it, it moves back down to the height it was just previously at (just above the height of the keyboard, which is now dismissed). The next time I edit text, it moves up again, but not the full keyboard.height amount for some reason. When I dismiss, it goes down the FULL keyboard height. This then repeats until the UIView falls off the bottom of the screen. This movement is so strange and I have no idea what the problem is. All I wanted is for it to move up and down with the keyboard. Any ideas how to fix this? thanks
Upvotes: 0
Views: 1952
Reputation: 534893
You are doing too many things wrong for me to list, so I have just fixed your project and made a pull request. Merge the pull request into your repo and you will see that it now works fine.
Just for the record, here are some of the main things you were doing wrong:
You added a bottom constraint, in code, to the blue view. But you already had a bottom constraint on the blue view. Thus you now have two of them, and any change in one of them will cause a conflict. The Xcode console was telling you very clearly that this was happening, but you ignored what it told you.
You were changing the constraint constant but also changing the blue view center
. That probably caused no harm but it was pointless. You cannot govern a view's position by its center
if you are governing it with constraints; they are opposites.
In your show and hide methods you examined keyboardFrameBeginUserInfoKey
. That's wrong. You want to examine keyboardFrameEndUserInfoKey
. The question is not where the keyboard is now but where it will be when it finishes moving.
The animation is wrong. There is no need for a UIView animation; you are already in an animation block. Just call layoutIfNeeded
and the animation will happen together with the movement of the keyboard.
Your entire way of speaking of and accessing constraints is wrong. You use an incorrect expression super.view
(you probably meant self.view
). But even more important, you attempt to access the desired constraint by saying self.constraints[2]
. That sort of thing is fragile in the extreme. The correct approach is to keep a reference to the actual constraint (an instance property). In this situation, since the constraint already exists (in the storyboard), that reference can be an outlet.
So, with all that said, here's my rewrite of your code; this is the complete code needed:
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()
}
}
}
With all of that said, the code is still wrong, because you are not taking account of the very real possibility that you will get a keyboardWillShow
notification when the keyboard is already showing. However, I leave that for your later investigation.
Upvotes: 1