Why does the whole view jerk up when the keyboard hides?

484 Views Asked by At

When the keyboardWillHideFunction is called the whole view from the viewController jerks upward. Below is a video of the probelm occuring.

vidoe of screen jerking

Below is the code I am using to control keyboard notifications. I have tried playing around with the animation times and the layout constraints but I havent been able to get much further. Any suggestions on how to fix it thank you.

EDITED CODE BASED ON SUGGESTIONS- Ive tried creating a variable for the layout constraints as suggested however the problem still persists any ideas why?

import UIKit
import Foundation

extension UIView {
    func currentFirstResponder() -> UIResponder? {
        if self.isFirstResponder {
            return self
        }

        for view in self.subviews {
            if let responder = view.currentFirstResponder() {
                return responder
            }
        }

        return nil
    }
}

extension Notification.Name{
    static let showKeyboard = Notification.Name("showKeyboard")
}

class KeyboardSlider: NSObject {
    // variables to hold and process information from the view using this class
    weak var view: UIView?
    var searchBarTopTags:SearchBarTopTagsViewController?
    var amountToShiftBy:CGFloat!
    var originalFrame:CGRect!
    var previewController:PreviewController!

    @objc func keyboardWillShow(notification: NSNotification) {
self.searchBarTopTags?.myViewBottomLayoutConstraint.constant =  -self.getKeyboardHeight(notification as! Notification) + previewController.view.safeAreaInsets.bottom
        UIView.animate(withDuration: 0, animations: {
            self.view?.layoutIfNeeded()
            self.searchBarTopTags?.myView.layoutIfNeeded()

        })
    }

    @objc func keyboardWillHide(notification:NSNotification){

        self.searchBarTopTags?.myViewBottomLayoutConstraint.constant = 0
        UIView.animate(withDuration: 0, animations: {
            self.view?.layoutIfNeeded()
            self.searchBarTopTags?.myView.layoutIfNeeded()
        })
    }
    func getKeyboardHeight(_ notification:Notification) -> CGFloat {
        // get exact height of keyboard on all devices and convert to float value to return for use
        let userInfo = notification.userInfo
        let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
        return keyboardSize.cgRectValue.height
    }

    func subscribeToKeyboardNotifications(view: UIView) {
        // assigning view to class' counterpart
        self.view = view
        // when UIKeyboardWillShow do keyboardWillShow function
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
    }


    func subscribeToKeyboardNotifications(view: UIView, previewController:PreviewController? = nil) {
        // assigning view to class' counterpart
        self.view = view
        self.searchBarTopTags = previewController?.searchBarTopTags
        self.previewController = previewController

        // when UIKeyboardWillShow do keyboardWillShow function
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: .UIKeyboardWillHide, object: nil)
    }


    func unsubscribeFromKeyboardNotifications() {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
    }

}

ORIGINAL CODE:

import UIKit
import Foundation

extension UIView {
    func currentFirstResponder() -> UIResponder? {
        if self.isFirstResponder {
            return self
        }

        for view in self.subviews {
            if let responder = view.currentFirstResponder() {
                return responder
            }
        }

        return nil
    }
}

extension Notification.Name{
    static let showKeyboard = Notification.Name("showKeyboard")
}

class KeyboardSlider: NSObject {
    // variables to hold and process information from the view using this class
    weak var view: UIView?
    var searchBarTopTags:SearchBarTopTagsViewController?
    var amountToShiftBy:CGFloat!
    var originalFrame:CGRect!

    @objc func keyboardWillShow(notification: NSNotification) {
        self.originalFrame = self.searchBarTopTags?.myView.frame
        self.amountToShiftBy = (self.searchBarTopTags?.view.frame.maxY)! - self.getKeyboardHeight(notification as! Notification) - (self.searchBarTopTags?.myView.frame.height)!
        self.amountToShiftBy = (searchBarTopTags?.view.bounds.height)! - self.getKeyboardHeight(notification as! Notification) - (searchBarTopTags?.myView.bounds.height)!
        self.searchBarTopTags?.myView.bottomAnchor.constraint(equalTo: (self.searchBarTopTags?.view.bottomAnchor)!, constant: -self.amountToShiftBy).isActive = true

        UIView.animate(withDuration: 0, animations: {
            self.view?.layoutIfNeeded()
            self.searchBarTopTags?.view.layoutIfNeeded()
            self.searchBarTopTags?.myView.layoutIfNeeded()        })

    }

    @objc func keyboardWillHide(notification:NSNotification){

        self.searchBarTopTags?.myView.bottomAnchor.constraint(equalTo: (self.searchBarTopTags?.view.bottomAnchor)!, constant: 0).isActive = true
        UIView.animate(withDuration: 0, animations: {
            self.view?.layoutIfNeeded()
            self.searchBarTopTags?.view.layoutIfNeeded()
            self.searchBarTopTags?.myView.layoutIfNeeded()
        })
    }
    func getKeyboardHeight(_ notification:Notification) -> CGFloat {
        // get exact height of keyboard on all devices and convert to float value to return for use
        let userInfo = notification.userInfo
        let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
        return keyboardSize.cgRectValue.height
    }

    func subscribeToKeyboardNotifications(view: UIView) {
        // assigning view to class' counterpart
        self.view = view
        // when UIKeyboardWillShow do keyboardWillShow function
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
    }


    func subscribeToKeyboardNotifications(view: UIView, seachBarTopTagsVC:SearchBarTopTagsViewController? = nil) {
        // assigning view to class' counterpart
        self.view = view
        self.searchBarTopTags = seachBarTopTagsVC

        // when UIKeyboardWillShow do keyboardWillShow function
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: .UIKeyboardWillHide, object: nil)
    }


    func unsubscribeFromKeyboardNotifications() {
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
    }

}
2

There are 2 best solutions below

2
Shehata Gamal On

Inside keyboardWillShow there is

self.searchBarTopTags?.myView.bottomAnchor.constraint(equalTo: (self.searchBarTopTags?.view.bottomAnchor)!, constant: -self.amountToShiftBy).isActive = true

And inside keyboardWillHide

self.searchBarTopTags?.myView.bottomAnchor.constraint(equalTo: (self.searchBarTopTags?.view.bottomAnchor)!, constant: 0).isActive = true

which off course will cause a conflict as the keyboard hides/shows , you need to create the bottom constraint only once say in viewDidLoad , then play with the constant value inside those methods , like this

var bottomConstraint:NSLayoutConstraint!

//

bottomConstraint = self.searchBarTopTags?.myView.bottomAnchor.constraint(equalTo: (self.searchBarTopTags?.view.bottomAnchor)!, constant: 0)
bottomConstraint.isActive = true

//

@objc func keyboardWillShow(notification: NSNotification) {

   // other code
   bottomConstraint.constant = -self.amountToShiftBy
}

@objc func keyboardWillHide(notification: NSNotification) {

   bottomConstraint.constant = 0 
   // other code
}
1
ukim On

Each time you call the following line, a new constraint will be added:

self.searchBarTopTags?.myView.bottomAnchor.constraint(equalTo: (self.searchBarTopTags?.view.bottomAnchor)!, constant: -self.amountToShiftBy).isActive = true

You need to add a reference to the constraint and just change its constant.

let constraint = self.searchBarTopTags?.myView.bottomAnchor.constraint...
contraint.constant = ...