Swift Tricks

My own, mostly internal blog of Swift tips and tricks

Keyboard support for UITableViews and other scrollable views

The on screen keyboard can cause issues when you are viewing scrollable content -namely that the scrollable content can be hidden (and inaccessible) by the keyboard. To solve this problem, you need to change the content inset when the keyboard appears (and when it is hidden).

open class BaseTableView: UITableView {

    // MARK: - Initializer methods
    override init(frame: CGRect, style: UITableView.Style) {
        super.init(frame: frame, style: style)
        self.initKeyboard()
    }
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.initKeyboard()
    }
    deinit {
        self.deinitKeyboard()
    }

    /// Initialize and deinitialize the keyboard
    private func initKeyboard() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    private func deinitKeyboard() {
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    // MARK: - Keyboard notifications
    
    /// Keyboard will be displayed
    @objc func keyboardWillShow(_ notification:Notification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            self.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
        }
    }
    
    /// Keyboard will be hidden
    @objc func keyboardWillHide(_ notification:Notification) {
        self.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        // If the content size is smaller than the available space then scroll to top
        if self.contentSize.height <= self.frame.height {
            self.setContentOffset(.zero, animated: true)
        }
    }

    // ...
}