Search code examples
iosswiftxcodeuikitnslayoutconstraint

Xcode giving error when I programmatically change constraints


I have a tableview that I've initialized with 4 constraints in the viewDidLoad() method. I wanted to programmatically change the bottomAnchor later in the code so I saved it as a variable bottomAnchor I then have another variable keyboardBottomAnchor for the changed constraint

These are the initial constraints:

tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 50).isActive = true
tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
bottomAnchor = tableViewe.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant:  (-1) * card.cardHandleAreaHeight + -20)
bottomAnchor.isActive = true

Basically, I wanted the table view to go up when the keyboard open and back down when the keyboard closes. Here's how that looks:

@objc func keyboardAppears(notification: Notification) {
        let userInfo = notification.userInfo
        keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
       
        //this is the original constraint
        self.bottomConstraint.isActive = false
        //Here I make a new variable to save the new constraint
        keyboardBottomConstraint = tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -1 * keyboardFrame.height)
        keyboardBottomConstraint.isActive = true
}

@objc func keyboardDisappears(notification: Notification) {
        NSLayoutConstraint.deactivate([keyboardBottomConstraint])
        bottomConstraint.isActive = true
}

The keyboardAppears method is working (the table view goes up when the keyboard shows) but the keyboardDisappears method is giving me a Unable to simultaneously satisfy constraints error (aka it is saying that both bottomConstraint and keyboardBottomConstraint are active)

Any thoughts as to why this is happening?

UPDATE:

I used the .constant below (this works but only the first time I open up the keyboard)

@objc func keyboardAppears(notification: Notification) {
        let userInfo = notification.userInfo
        var keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
        
        bottomConstraint.constant = -1 * keyboardFrame.height
        tableView.scrollUp()
}

@objc func keyboardDisappears(notification: Notification) {
        returnOriginalConstraint()
}

func returnOriginalConstraint() {
        bottomAnchor.constant = (-1) * buttonCard.cardHandleAreaHeight + -20
}

//scrolling method
func scrollUp() {
        DispatchQueue.main.async {
            self.entrySpace.scrollToRow(at: IndexPath(row: self.data.count - 1, section: 0), at: .top, animated: true)
        }
}

Solution

  • Swift 5

    Do not deactivate the constraint, just change its constant value.

    I have written the code below and tested it, customize it as you like, will work perfectly with you!

    import UIKit
    
    class ViewController: UIViewController
    {
        // MARK: Properties
    
        var tableView: UITableView!
        var tableBottomConstraint: NSLayoutConstraint!
    
        // MARK: View Controller Life Cycle
    
        override func viewDidLoad()
        {
            super.viewDidLoad()
            addKeyboardObservers()
            setupTableView()
        }
    
        deinit
        {
            removeKeyboardObservers()
        }
    
        // MARK: Methods
    
        private func addKeyboardObservers()
        {
            NotificationCenter.default.addObserver(self,
                                                   selector: #selector(keyboardWillShow(notification:)),
                                                   name: UIResponder.keyboardWillShowNotification,
                                                   object: nil)
    
            NotificationCenter.default.addObserver(self,
                                                   selector: #selector(keyboardWillHide(notification:)),
                                                   name: UIResponder.keyboardWillHideNotification,
                                                   object: nil)
        }
    
        private func removeKeyboardObservers()
        {
            NotificationCenter.default.removeObserver(self,
                                                      name: UIResponder.keyboardWillShowNotification,
                                                      object: nil)
    
            NotificationCenter.default.removeObserver(self,
                                                      name: UIResponder.keyboardWillHideNotification,
                                                      object: nil)
        }
    
        private func setupTableView()
        {
            tableView = UITableView()
            tableView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(tableView)
    
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true
            tableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
    
            tableBottomConstraint = tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50.0)
            tableBottomConstraint.isActive = true
        }
    
        // MARK: Keyboard Handling
    
        @objc func keyboardWillShow(notification: Notification)
        {
            if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
            {
                tableBottomConstraint.constant = -1 * keyboardFrame.height
                loadViewIfNeeded()
            }
        }
    
        @objc func keyboardWillHide(notification: Notification)
        {
            tableBottomConstraint.constant = -50.0
            loadViewIfNeeded()
        }
    }
    

    Note: Do not forget to remove keyboard observers in deinit