1

I have a tableView in my view controller and I have it such that the tableView shrinks and expands depending on whether the keyboard is on the screen or off the screen. When the tableView shrinks, I want it to scroll to the bottom row but I can't get this to work.

Currently, I have the following:

@objc func keyboardAppears(notification: Notification) {
        if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
                bottomConstraint.constant = -1 * keyboardFrame.height
                view.loadViewIfNeeded()
                tableView.scrollToRow(at: IndexPath(row: 10, section: 0), at: .top, animated: true)
         }
}

I also tried putting it in the main thread. This got it to work the first time the keyboard went up only. The code looked as follows:

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

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

UPDATE:

The following works after a letter is typed in the field (I want it to work right when the keyboard comes up)

NOTE: this is the UITableViewCell class because the cell is the last item in the UITableView

import UIKit

class EntryButtonCell: UITableViewCell {
    
    //the textView for new entries
    var entryCell = UITextView()
    
    //Reference to the parent table
    var parentTable = UITableView()

    //Reference to the parent view (the one that holds the table view)
    var parentView = ParentVC()

    var bottomConstraint = NSLayoutConstraint()
    
    //Initialization
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        //set this cell as the delegate for the entry textView
        entryCell.delegate = self
        
        //deactivate interaction with the cell so the user can interact with the textView
        contentView.isUserInteractionEnabled = false
        
        //set up the textView
        prepTextView()
        
        //set the notification for the keyboard events
        setKeyboardObservers()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    //deinitialization
    deinit {
        //remove the keyboard observers
        removeKeyboardObservers()
    }
    
    func prepTextView() {
        //just did the constraints here 
        //removing to save space
    }
    
    func scrollToBottom() {
        DispatchQueue.main.async { [weak self] in
            guard let data = self?.parentView.data else { return }
            let indexPath = IndexPath(row: data.count, section: 0)
            self?.parentTable.scrollToRow(at: indexPath, at: .bottom, animated: true)
        }
    }
    
    //set the keyboard notification
    private func setKeyboardObservers() {
        //add keyboardWillShowNotification
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardAppears(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        
        //add keyboardWillHideNotification
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardDisappears(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    //deactivate the keyboard notifications
    private func removeKeyboardObservers() {
       //deactivate keyboardWillShowNotification
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        
        //deactivate keyboardWillHideNotification
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    //run when keyboard shows
    @objc func keyboardAppears(notification: Notification) {
        if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
            
                scrollToBottom()
                bottomConstraint.constant = -1 * keyboardFrame.height
                parentTable.layoutSubviews()
                parentView.loadViewIfNeeded()
        }
    }

    @objc func keyboardDisappears(notification: Notification) {
        tableBottomConstraint.constant = -100.0
        parentView.loadViewIfNeeded()
    }

    //Took out a few of the textView methods to save space
}
green
  • 77
  • 7

2 Answers2

0

Swift 5

I have tested the following code based on your logic so that you just add a few lines to get it to work.

// MARK: Keyboard Handling

@objc func keyboardWillShow(notification: Notification)
{
    if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
    {
        tableView.reloadData()
        scrollToBottom()
        
        tableBottomConstraint.constant = -1 * (keyboardFrame.height + 100)
        loadViewIfNeeded()
    }
}

@objc func keyboardWillHide(notification: Notification)
{
    tableBottomConstraint.constant = -100.0
    loadViewIfNeeded()
}

func scrollToBottom()
{
    DispatchQueue.main.async { [weak self] in
        guard let data = self?.data else { return }
        let indexPath = IndexPath(row: data.count - 1, section: 0)
        self?.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
    }
}

Result

The output of my code will be something like this:

enter image description here

enter image description here

Essam Fahmi
  • 1,920
  • 24
  • 31
  • Thanks for the help, for some reason it's still acting a little strange. I'm getting the error "Message from debugger: Terminated due to signal 9" – green Oct 05 '20 at 19:36
  • It works with no problems. Paste your code and I will test it – Essam Fahmi Oct 06 '20 at 11:58
  • Sorry for the delay, I'll try again and paste my code if there is an error – green Oct 08 '20 at 23:33
  • Almost there:) Its looking much better. Only problem now is that it scrolls after I type a letter? So I'll type a letter, then it scrolls (as opposed to just scrolling from the beginning when the keyboard goes up) @Essam – green Oct 09 '20 at 01:37
  • I also added the code underneath the initial question @Essam – green Oct 09 '20 at 02:38
  • @green man, just copy the code I wrote and it will work with you. I tested it! – Essam Fahmi Oct 09 '20 at 11:16
0

After updating a constraint, you have to commit the update by calling the layoutSubview() and after that you can call the scrollToRow(at:at:animated) method.

@objc func keyboardAppears(notification: Notification) {
        if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
            bottomConstraint.constant = -keyboardFrame.height
            view.layoutSubviews()
            self.entrySpace.scrollToRow(at: IndexPath(row: self.data.count - 1, section: 0), at: .top, animated: true)
        }
    }
Blazej SLEBODA
  • 8,936
  • 7
  • 53
  • 93
  • Thanks for the help, I tried this and it slightly worked. The only challenge is that the tableView is hidden behind the keyboard until the keyboard is used. So (1) the keyboard comes up, some stuff is hidden (2) a letter is typed, everything scrolls up. Is there any way to get it to scroll from the beginning as opposed to after a letter is typed? – green Oct 05 '20 at 19:45