1

I would like to increase the height of a tableview whenever the keyboard disappears. I have a tableview that is populated from the bottom upwards. The tableview is not filled entirely with data, meaning there is some empty space at the top of the tableview. There will be some cells in the tableview. Since the table view is populated from the bottom upwards, these cells should be towards the bottom of the tableview. When the keyboard is shown, the total height of the tableview is about half of the screen. When the keyboard disappears, the total height of the tableview should be almost the whole screen. This means that when the keyboard disappears, the cells should start from further down on the screen.

This is what it looks like when there are two cells in the tableview:

enter image description here

This is what it looks like when two more cells have been added to the tableview:

enter image description here

This is what it looks like when the keyboard disappears:

enter image description here

When the keyboard disappears, the four cells should start from the bottom of the screen, not from halfway up the screen. The four cells should move downwards so that they start from where the gray line at the bottom of the screen is. There should not be a big gap between where the first cell (which says "Apple") is and where the gray line at the bottom of the screen is. At the same time, the tableView itself should be bigger. This means that while the top of the tableview remains in the same place, the bottom of the tableview is now lower than it was before, because the keyboard is now not taking up approximately half of the screen. How can I do this? Thanks.

Here is my code right now:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var textField: UITextField!
    @IBOutlet weak var lowerGrayLine: UIView!

    @IBOutlet weak var tableView: UITableView!

    var fruits = [FruitModel]()

    override func viewDidLoad() {
        super.viewDidLoad()

        //Adjust height of the lower Gray Line
        lowerGrayLine.frame.origin.y = 411

        //No dividing lines in the tableview initially
        self.tableView.separatorStyle = .none

        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 140

        if (self.tableView(self.tableView, numberOfRowsInSection: 0) > 0) 
        {
            self.updateTableContentInset()
        }

        // Allow for keyboard dismissal
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard))
        view.addGestureRecognizer(tap)

        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

        // Show keyboard
        textField.becomeFirstResponder()
    }

    func keyboardWillShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            //Adjust text box, and dividing line placement
            textField.frame.origin.y = 416
            lowerGrayLine.frame.origin.y = 411
        }
    }

    func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            textField.frame.origin.y = 597
            lowerGrayLine.frame.origin.y = 557
        }
    }

    //Calls this function when the tap is recognized.
    func dismissKeyboard() {
        //Causes the view (or one of its embedded text fields) to resign the first responder status.
        view.endEditing(true)
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.fruits.count;
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "fruitCell"
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? FruitTableViewCell else {
            fatalError("The dequeued cell is not an instance of FruitTableViewCell.")
        }

        var fruitName = fruits[indexPath.row]
        var fruitNamename = fruitName.name

        var colon = ":"

        cell.nameLabel.text = fruitNamename + colon

        cell.fruitLabel.text = fruitName.fruit

        //Make some adjustments to make a line appear when a fruitCell is actually being shown
        cell.preservesSuperviewLayoutMargins = false
        cell.separatorInset = UIEdgeInsets.zero
        cell.layoutMargins = UIEdgeInsets.zero

        // This causes the bottom-most cell to not have a cell separator 
        if (indexPath.row == fruits.count-1) {
            cell.separatorInset = UIEdgeInsetsMake(0, cell.bounds.size.width, 0, 0);
        }
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }

    // Adjust the tableView
    func updateTableContentInset() {
        let numRows = tableView(tableView, numberOfRowsInSection: 0)

        var contentInsetTop = tableView.bounds.size.height
        for i in 0..<numRows {
            contentInsetTop -= tableView(tableView, heightForRowAt: IndexPath(item: i, section: 0))
            if contentInsetTop <= 0 {
                contentInsetTop = 0
            }
        }
        self.tableView.contentInset = UIEdgeInsetsMake(contentInsetTop, 0, 0, 0)
        self.tableView.scrollToRow(at: IndexPath(item: numRows-1, section: 0), at: .bottom, animated: false)
        //Prevent scrolling
        self.tableView.bounces = false;
        //When we have actual fruit Cells to show, we want the line divisions
        self.tableView.separatorStyle = .singleLine
    }
}

I have tried adding the following code to my keyboardWillShow function:

var tableViewFrame: CGRect!
tableViewFrame = self.tableView.frame
tableViewFrame.size.height += 146.0
tableView.frame = tableViewFrame
tableView.contentInset = UIEdgeInsetsMake(146.0, 0, 0, 0);

However, when the keyboard disappears, there are horizontal gray lines that span the screen, as shown below:

EDIT:

enter image description here

These horizontal gray lines (six in total) should not be there. How do I prevent them from appearing when the keyboard has disappeared?

Everett
  • 387
  • 1
  • 4
  • 17

3 Answers3

1

The simplest solution is IQKeyboardManager. This will manage the space below keyboard for you.

Otherwise, add a bottom constraint tableViewBottomConstraint to the table view. Set its initial value tableViewBottomConstraint.constant = 0

Now on set it equal to the keyboard height + padding in keyboardWillShow keyboardWillHide

func keyboardWillShow(notification: NSNotification) {
    tableViewBottomConstraint.constant = keyboardHeight + padding
    layoutTableView()
}

func keyboardWillHide(notification: NSNotification) {
    tableViewBottomConstraint.constant = 0
    layoutTableView()
}

private func layoutTableView() {
    UIView.animate(withDuration: 0.3, animations: {
        tableView.superview?.layoutIfNeeded()
    }, completion: nil) 
}
Satish
  • 2,015
  • 1
  • 14
  • 22
  • Should this bottom constraint be between the tableview and its nearest lower neighbor, which is the horizontal gray line? Or, should this bottom constraint be between the tableview and the bottom of the entire screen, which is the Bottom Layout Guide? – Everett May 23 '18 at 00:15
  • That should be between tableView and Bottom Layout Guide if you don't have any other element/view below tableView. If you have one below tableView then that bottom constraint should be between that element and Bottom Layout Guide. – Satish May 23 '18 at 06:03
0

To do this you need to write your code in keyboardWillHide:

  1. Resize the tableView increasing its frame's height (or the relative constraint's constant value) of the keyboard's height;

  2. Key point: set tableView's top content inset equal to the keyboard's height.

So for instance if the keyboard's height is 216pt, you will do:

CGRect tableViewFrame = tableView.frame;
tableViewFrame.size.height += 216.0;
tableView.frame = tableViewFrame;

tableView.contentInset = UIEdgeInsetsMake(216.0, 0, 0, 0);

If you are using constraints instead, the only difference is that you'll change the height of the constraint's constant relative to the height of the tableView.

P.s. if you want a deeper explanation of what UIEdgeInsets is, take a look here: https://developer.apple.com/documentation/uikit/uiedgeinsets They have been made exactly to create empty space with a specific offset.

Matteo Gobbi
  • 17,697
  • 3
  • 27
  • 41
  • I have added your code above. However, when the Table View disappears, there are horizontal gray lines that span the screen. I have edited my original post to include a screenshot of what my screen looks like in this case. Instead, the screen should be just white. How do I prevent these gray lines from appearing? – Everett May 23 '18 at 00:02
0
    func keyboardWillShow(notification:NSNotification) {

          if let keyboardSize = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? CGRect {
        let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: 
         keyboardSize.height, right: 0)
        print(contentInsets)


        btnBottomConstrain.constant = contentInsets.bottom
        UIView.animate(withDuration: 0.2, delay: 0, options: .transitionCurlDown, animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)

    }
}

func keyboardWillHide(notification:NSNotification) {



    if let keyboardSize = notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? CGRect {
        let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
        print(contentInsets)

        self.btnBottomConstrain.constant = 0

        UIView.animate(withDuration: 0.2, delay: 0, options: .transitionCurlDown, animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)


    }
}

replace these two methods with your keyboardshow and keybordhide methods accordingly. here btnBottomConstrain will be the outlet of your tableview's bottom constraint. I hope this will work for you.

Mahesh Dangar
  • 806
  • 5
  • 11
  • I have tried your code above. I added a bottom constraint between the tableview and the bottom of the screen, the Bottom Layout Guide. However, after adding this, when the keyboard disappears, there are horizontal gray lines that span the screen. There should be no gray lines. I have edited my original post to include a screenshot of these gray lines. How do I prevent these gray lines from appearing? – Everett May 23 '18 at 00:46
  • 1
    when the tableView is empty, just hide it. – Matteo Gobbi May 23 '18 at 23:31