1

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)
        }
}
6
  • Creating a new constraint isn't necessary. Simply update the constant property of the constraint you already have. You can animate this change. Commented Oct 2, 2020 at 21:40
  • Thanks that helped partially:) Now, it only work the first time I make the keyboard appear/disappear but doesn't work the second time around. Any idea why? Commented Oct 2, 2020 at 21:46
  • Did you set the constant back when the keyboard disappears? I can't say anything more without seeing your new code. What notifications are you observing? Have you set breakpoints or log statements to see what functions are being called? Commented Oct 2, 2020 at 21:49
  • Not the answer to your question but if you want to have a view to avoid keyboard I suggest you to use IHKeyboardAvoiding Commented Oct 2, 2020 at 21:51
  • I added the code with the constants to the post, as mentioned this only works the first time around? Also, I'm observing keyboardWillHideNotification and keyboardWillShowNotification Commented Oct 2, 2020 at 21:56

1 Answer 1

0

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

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you - been stick on that awhile:) If possible I have a follow-up question: I want the table to scroll to the bottom when it goes up so I'm using tableView.scrollToRow. Sadly, it only works the first time around.
@green First of all, mark the answer accepted if it helped you. Regarding your question, please provide more details with code and I will help you.
Marked it green! I just want to add self.entrySpace.scrollToRow(at: IndexPath(row: self.data.count - 1, section: 0), at: .top, animated: true) so that it scrolls the content up when the keyboard comes up - was wondering how to place it in the above?
Yes, you should mark the answer as accepted and vote it up if you can when you got your answer. Regarding your question, put your code in keyboardWillShow so that whenever the keyboard appears you scroll.
Thanks, and I'll make sure to mark it going forward. I was still having trouble with the scrolling so I went ahead and created another question for it here. Thanks for the help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.