Tableview scroll content when keyboard shows

50,133

Solution 1

Try keeping the editing index path editingIndexPath Getting index path and scroll tableview to that index path

func keyboardWasShown (notification: NSNotification)
    {
        println("keyboard was shown")
        var info = notification.userInfo
        var keyboardSize = info.objectForKey(UIKeyboardFrameBeginUserInfoKey).CGRectValue().size

        var contentInsets:UIEdgeInsets

        if UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation) {

            contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0);
        }
        else {
            contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.width, 0.0);

        }

        myTableView.contentInset = contentInsets

        myTableView.scrollToRowAtIndexPath(editingIndexPath, atScrollPosition: .Top, animated: true)
        myTableView.scrollIndicatorInsets = myTableView.contentInset
    }

Solution 2

override func viewDidLoad() {
    super.viewDidLoad()

    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)

}

func keyboardWillShow(_ notification:Notification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
    }
}
func keyboardWillHide(_ notification:Notification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    }
}

Solution 3

For Swift 4.2

In viewDidLoad() of your UIViewController:

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)

And implementation of selectors:

@objc private func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
    }
}

@objc private func keyboardWillHide(notification: NSNotification) {
    tableView.contentInset = .zero
}

Solution 4

Use this Awesome Extension (Updated for Swift 4.2),

extension UIViewController {

    func registerForKeyboardWillShowNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil) {
        _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil, using: { notification -> Void in
            let userInfo = notification.userInfo!
            let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
            let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top, left: scrollView.contentInset.left, bottom: keyboardSize.height, right: scrollView.contentInset.right)

            scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
            block?(keyboardSize)
        })
    }

    func registerForKeyboardWillHideNotification(_ scrollView: UIScrollView, usingBlock block: ((CGSize?) -> Void)? = nil) {
        _ = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: nil, using: { notification -> Void in
            let userInfo = notification.userInfo!
            let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.size
            let contentInsets = UIEdgeInsets(top: scrollView.contentInset.top, left: scrollView.contentInset.left, bottom: 0, right: scrollView.contentInset.right)

            scrollView.setContentInsetAndScrollIndicatorInsets(contentInsets)
            block?(keyboardSize)
        })
    }
}

extension UIScrollView {

    func setContentInsetAndScrollIndicatorInsets(_ edgeInsets: UIEdgeInsets) {
        self.contentInset = edgeInsets
        self.scrollIndicatorInsets = edgeInsets
    }
}

and use as mentioned below from the respective ViewController,

@IBOutlet weak var tableview: UITableView!

override func viewDidLoad() {
        super.viewDidLoad()
        registerForKeyboardWillShowNotification(tableview)
        registerForKeyboardWillHideNotification(tableview)

        /* use the above functions with
           block, in case you want the trigger just after the keyboard
           hide or show which will return you the keyboard size also.
         */

        registerForKeyboardWillShowNotification(tableView) { (keyboardSize) in
            print("size 1 - \(keyboardSize!)")
        }
        registerForKeyboardWillHideNotification(tableView) { (keyboardSize) in
            print("size 2 - \(keyboardSize!)")
        }

    }

Solution 5

For Swift 5.0 and considering iPhone Predictive Text is On

take "keyboardSize.height + tableView.rowHeight" as the bottom of tableview just in case if iPhone Predictive Text is On. In that case, we need to scroll up tableview little more.

    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
        
        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)
    }
    
    @objc private func keyboardWillShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height + tableView.rowHeight, right: 0)
        }
    }

    @objc private func keyboardWillHide(notification: NSNotification) {
        tableView.contentInset = .zero
    }
Share:
50,133
msalafia
Author by

msalafia

Updated on May 28, 2020

Comments

  • msalafia
    msalafia almost 4 years

    I have a table view with a text field and a textview. I've implemented this code like suggested by this apple sample code https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

    @IBOutlet var myTableView: UITableView
    func keyboardWasShown (notification: NSNotification)
    {
        println("keyboard was shown")
        var info = notification.userInfo
        var keyboardSize = info.objectForKey(UIKeyboardFrameBeginUserInfoKey).CGRectValue().size
    
        myTableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
        myTableView.scrollIndicatorInsets = myTableView.contentInset
    }
    
    func keyboardWillBeHidden (notification: NSNotification)
    {
        println("keyboard will be hidden")
        myTableView.contentInset = UIEdgeInsetsZero
        myTableView.scrollIndicatorInsets = UIEdgeInsetsZero
    }
      override func viewDidLoad() {
    
        super.viewDidLoad()
    
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    
    }
    

    When i click on the "text" of the scroll view go just above the top of the screen, but when i release the keyboard it remains scrolled up. It's just like the insets property can't be modified after the first time. What's my mistake?

  • msalafia
    msalafia almost 10 years
    It doesn't work! it still remain fixed after first scroll. I don't understand why the assignments at keyboardWillBeHidden function doesnt work. Is it possible because the tableview is built with Interface Builder and maybe there's some particular option?
  • Yatheesha
    Yatheesha almost 10 years
    @Andorath Can you pass me the sample ?
  • msalafia
    msalafia almost 10 years
    how can i sand you the sample?
  • Nupur Gupta
    Nupur Gupta over 6 years
    I am facing the same issue. Can anyone suggest.
  • Ariel Antonio Fundora
    Ariel Antonio Fundora over 6 years
    This is the best answer. Thanks.
  • Aftab Ahmed
    Aftab Ahmed about 6 years
    The problem with the above approach is the first click on the table view after setting the contentInset, in the section > 0 points to section = 0. i.e. at didSelect the indexPath values correspond to the values in section 0, though you have clicked on the rows in the section > 0. This happens only for the first click.
  • Disha
    Disha almost 6 years
    Please check this link. I hope it will help you code.tutsplus.com/tutorials/…
  • yoninja
    yoninja over 5 years
    Same as the answer of Zany.
  • Gajendra Rawat
    Gajendra Rawat over 5 years
    Plz also remove notification observer
  • ooxio
    ooxio over 5 years
    I noticed that you may receive wrong height if you first close the keyboard and then open it again. To avoid this, simply use UIKeyboardFrameEndUserInfoKey instead of UIKeyboardFrameBeginUserInfoKey.
  • Peter Kreinz
    Peter Kreinz over 5 years
    Nice extension. Please update to Swift 4.2 and show an example using the block.Thx.
  • Soumen
    Soumen over 5 years
    Updated the answer for Swift 4.2
  • John J. Camilleri
    John J. Camilleri about 5 years
    There's a missing } there before extension UIScrollView (and the indentation is slightly off)
  • John J. Camilleri
    John J. Camilleri about 5 years
    Really great extension, but does anyone else notice that the value for the bottom edge inset isn't quite right? I end up with extra dead space between my view and the keyboard (see here). The view is aligned to the safe area. I have to adjust the keyboardSize.height value with the function return height - (height > 200 ? 50 : 30).
  • Ben Shabat
    Ben Shabat almost 5 years
    this is just working great, just need to add the scroll to row function
  • DisplayName
    DisplayName over 4 years
    I removed the DispatchQueue part bc it's not needed. It works for me. Share some of your code.
  • captain_haddock
    captain_haddock over 4 years
    Best answer, can I also suggest to deinit the notifications, please. deinit { NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) }
  • Kurt Lane
    Kurt Lane almost 4 years
    let indexPath:IndexPath = IndexPath(row: #, section: #) tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
  • Aman Gupta
    Aman Gupta almost 4 years
    I tried the same. But my textfield is not moving up
  • nickdnk
    nickdnk almost 3 years
    Note that removing observers is no longer required: stackoverflow.com/a/40339926/1650180
  • tounaobun
    tounaobun almost 3 years
    Works like a charm.
  • paul_f
    paul_f over 2 years
    This doesn't seem to perform well for me. The first time the keyboard shows it works, but the second time, the value returned by keyboardSize.height is like 100 less.
  • paul_f
    paul_f over 2 years
    Doesn't work at all for me.
  • CyberMew
    CyberMew about 2 years
    Take note to remove the observers or there might be a memory leak. See: developer.apple.com/library/archive/releasenotes/Foundation/‌​….