4

My app displays a form, and on some of the cells there are textfields where you can enter information. If the cell is towards the bottom of the screen, the cell will be completely blocked which means you can't see what the question is that you are answering. My UITableView is embedded in a UIViewController.

I've looked through many of the threads that propose answers to this problem and implemented some of the code. If I can get this method working, I believe my code will work:

func textFieldDidBeginEditing(_ textField: UITextField) {
    if (self.tableView.contentOffset.y == 0)
    {
        self.tableView.scrollToRow(at: self.tableView.indexPath(for: ), at: UITableViewScrollPosition, animated: true)
    }
}

Right now I'm trying to solve the problem of getting the active cell so that it can be inputted into self.tableView.indexPath(for: ).

How would I go about doing this? Is this the right way to go about moving the view above the keyboard?

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Alex Cauthen
  • 497
  • 5
  • 12
  • 32

5 Answers5

1

Use https://github.com/hackiftekhar/IQKeyboardManager in your app. It will keep the textfields and keyboard settings OK itself.

SNarula
  • 548
  • 3
  • 16
1

On viewWillAppear you register for UIKeyboardWillShow and UIKeyboardWillHide notifications:

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

then on keyboardWillShow you get the frame of the keyboard and change the inset of the tableView

func keyboardWillShow(notification: Notification) {
     guard let keyboardFrame = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue else { return }
       let convertedFrame = view.convert(keyboardFrame, from: nil)
       tableView.contentInset.bottom = convertedFrame.height
}

then in keyboardWillHide you change the bottom inset back to 0 and if desired scroll the tableView back to a certain row. This scrolls it back to the first row positioned at the top of the tableView.

 func keyboardWillHide(notification: Notification) {
    tableView.contentInset.bottom = 0
    tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
}

It's also a nice way to do it because it keeps your textField delegates uncluttered with code effecting the layout.

I believe I read somewhere that you don't need to remove yourself as observer for certain notifications which includes the keyboard notifications but to play safe you should put these in viewWillDisappear or deinit depending on situation

  deinit {
    NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillHide, object: nil)

     // If you don't have any other notifications you wan't to keep observing you can remove yourself from all observers by using this one line
     // NotificationCenter.default.removeObserver(self)
  } 
JustinM
  • 2,202
  • 2
  • 14
  • 24
1

Use NotificationCenter.

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

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillDisappear), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Jay Patel
  • 2,642
  • 2
  • 18
  • 40
0

Check out the answer to this post by me
iOS Support External Keyboard Without Displaying One

Also, for a tableView, I use something like this for displaying a cell appropriately with a keyboard.

func textFieldDidBeginEditing(_ textField: UITextField) {
    tableView.isScrollEnabled = false
    var path: IndexPath?

    path = theIndexPathOfTheCellWithTheTextField

    if path != nil {
        let rect = CGRect(x: 0, y: 0, width: tableView.frame.width, height: tableView.frame.height)
        tableView.tableFooterView = UIView(frame: rect)

        UIView.animate(withDuration: 0.6, animations: {
            [weak self] in
            self?.tableView.scrollToRow(at: path!, at: .top, animated: false)
        }, completion: nil)
    }
}

func textFieldDidEndEditing(_ textField: UITextField) {
    tableView.tableFooterView = UIView()
    tableView.isScrollEnabled = true
}

If you have a UI for the tableView's tableFooterView, than this part of the code may need to be altered or removed based on the available size for the footer view.

EDIT - More Info: In order to make an indexPath, you have a few options. You can just hard code it if you know the cells location like so:

//This works where neither theRowInt or theSectionInt are negative.
IndexPath(row: theRowInt, section: theSectionInt)

//So this would be the first section/first row
IndexPath(row: 0, section: 0)

The next way would be to base it on the cell you find through the textField:

let textFieldCell = textField.superview.superview.superview as! UITableViewCell
let indexPath = self.tableView.indexPathForCell(textFieldCell)

This works assuming that you it is the correct number of superviews up. If you place the textField directly into the contentView, than this should be the correct number since iOS 7 anyhow. If it crashes, than just change the number of .superviews till it works.

There are other methods as well, but if you are interested in them than research it yourself. Here is a good stack overflow post on the matter.

Community
  • 1
  • 1
Sethmr
  • 3,046
  • 1
  • 24
  • 42
  • How do you get theIndexPathOfTheCellWithTheTextField? Seems like if I could figure out that I could run with my above code. – Alex Cauthen Nov 29 '16 at 17:55
  • So I've tried this solution after having no luck with mine and another... and this one also isn't working. When I click a textfield the tableview does not scroll at all. Just a bit more information. I'm using custom tableviewcells that I created. The uitableview is placed in between 2 uiviews in my uiviewcontroller. I do not know if that has an effect on it scrolling correctly. Any thoughts on how I could debug this? – Alex Cauthen Dec 01 '16 at 16:37
0

By far the easiest solution to this problem is to use a UITableViewController rather than a UIViewController with a tableView property.

It auto-magically handles it for you. I'm well aware there are cases you can't use a UITableViewController, but when possible, you should. Using a container view controller that embeds a UITableViewController in a regular controller makes this feasible even in more complex situations.

Jordan Smith
  • 10,310
  • 7
  • 68
  • 114