6

Although I've searched, I'm confused about how best to approach this.

I have a tableView where the bottom cell is an input to the list, in the same way apple reminders work. When there are too many items on the list, the keyboard covers the list and I can't see what I'm typing.

My thought it I need to change the physical size of the table view and ensure it is scrolled to the bottom when the keyboard shows.

Some have said keyboard observers but the majority of code I've found for this is out of date and errors when put into Xcode.

NSNotificationCenter Swift 3.0 on keyboard show and hide

This is the best I can find but I'm also hearing about contraints, using a UITableViewController instead of embedding a UITableView and so on ...

This is the code I have so far:

NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)


@objc func keyboardWillShow(notification: Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        print("notification: Keyboard will show")
        if self.view.frame.origin.y == 0{
            self.view.frame.origin.y -= keyboardSize.height
        }
    }

}

@objc func keyboardWillHide(notification: Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y != 0 {
            self.view.frame.origin.y += keyboardSize.height
        }
    }
}

This moves the whole view up I think, which means safeareas (such as navigation bar and so on) have the TableView underneath them. Do I make the navigationview non-transparent in this approach?

Rob
  • 1,297
  • 2
  • 16
  • 30
  • Just increase the tableview's bottom constraint if you are embedding tableView, no need to modify the view size... – Tj3n Jan 31 '19 at 08:28
  • A hint: Since UITableView is a Subclass of UIScrollView you can use the contentInset property to achieve what you need. Read about it here: https://developer.apple.com/documentation/uikit/uiscrollview/1619406-contentinset – Teetz Jan 31 '19 at 08:32

1 Answers1

18

One solution (which I sometimes use) is simply change the content offset of the tableView when the keyboard appears/disappears. I believe this would work in your instance as opposed to varying the tableView's constraints as you mentioned your UIViewController is a UITableViewController. Please see the below code for my suggestion, hope this helps!

Handle Notifications:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    NotificationCenter.default.removeObserver(self)
}

Actions:

@objc func keyboardWillShow(notification: Notification) {
    if let keyboardHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
        print("Notification: Keyboard will show")
        tableView.setBottomInset(to: keyboardHeight)
    }
}

@objc func keyboardWillHide(notification: Notification) {
    print("Notification: Keyboard will hide")
    tableView.setBottomInset(to: 0.0)
}

Extensions:

extension UITableView {

    func setBottomInset(to value: CGFloat) {
        let edgeInset = UIEdgeInsets(top: 0, left: 0, bottom: value, right: 0)

        self.contentInset = edgeInset
        self.scrollIndicatorInsets = edgeInset
    }
}
WH48
  • 737
  • 1
  • 7
  • 24
  • Although this is partially working, my tableView needs to be manually scrolled to the correct position to have the cell with the text field appear on top of the keyboard. – btrballin Jun 02 '19 at 07:41
  • 1
    Have you tried tableView.scrollToRow(at: indexPath, at: .middle, animated: true) when the textField becomes first responder? If you use a delegate from the cell you will be able to get a reference to the cells indexPath from that function – WH48 Jun 02 '19 at 07:53
  • Gotcha. I already have a delegate function to update the data model to retain the textfield contents, so I used the `tableview.scrollToRow` function within my delegate function that gets called inside the `textViewDidChange(_ textView: UITextView)` function. So once the user types, it scrolls to focus in on that cell. – btrballin Jun 02 '19 at 08:21
  • Sounds great, perfect solution! – WH48 Jun 02 '19 at 09:00