1

I am trying to find a way to avoid keyboard to not block selected UITextField in dynamic UITableViews.

I am aware this is a common issue and there are a lot of answers to this. However, answers I found are either in Obj-C or not involving dynamic UITableView.

My table view being has its own UITableViewCell so UITextFields are connected to it.

Also, I want to refresh the cell when UITextField entry has been made. I can pass that info (I am guessing cell's IndexPath.row?) to the UIViewController and have it listen to change and apply reload cell method?

Here is the related code I have:

class favCell: UITableViewCell, UITextFieldDelegate {

    @IBOutlet weak var deciPadField: UITextField!

    @objc func doneClicked(){
        updateTotalWorth()
        deciPadField.resignFirstResponder()
        deciPadField.endEditing(true)
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
        deciPadField.becomeFirstResponder()
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let currentString: NSString = (textField.text ?? "") as NSString
        let newString = currentString.replacingCharacters(in: range, with: string)
        return  newString.count <= 10
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        updateTotalWorth()
        deciPadField.resignFirstResponder()
        return true
    }

    private func textViewDidEndEditing(_ textView: UITextView) {
        updateTotalWorth()
        deciPadField.endEditing(true)
    }
}

UPDATE:

If I can access my ViewControllers view from UITableViewCell, below code will do the trick:

func animateTextField(textField: UITextField, up: Bool) {
    let movementDistance:CGFloat = -130
    let movementDuration: Double = 0.3
    var movement:CGFloat = 0

    if up {
        movement = movementDistance
    } else {
        movement = -movementDistance
    }
    UIView.beginAnimations("animateTextField", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration)

    // Below line giving error, could not find the view. My ListVC.view should be referred to.
    self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

    UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) {
    self.animateTextField(textField: textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) {
    self.animateTextField(textField: textField, up:false)
}

Thank you!

Vetuka
  • 1,078
  • 1
  • 14
  • 34
  • try self.view.endediting() – Sagar Bhut Nov 09 '17 at 12:51
  • 1
    If you **(A)** subclass `UITableViewController`, it should be handled automatically for you. If you **(B)** are adding a `UITableView` as a subview of a `UIViewController`, you'll have to make the adjustments yourself when keyboard is shown / hidden. Are you doing **(A)** or **(B)**? – DonMag Nov 09 '17 at 13:12
  • I am doing B. I was aware of UITableViewController variance. – Vetuka Nov 09 '17 at 13:18
  • 1
    OK - then you have to handle it yourself. There are really so many examples out there already... read through this post and see if you can get it working. If not, come back and post the code you are trying to use: https://stackoverflow.com/questions/1126726/how-to-make-a-uitextfield-move-up-when-keyboard-is-present?rq=1 – DonMag Nov 09 '17 at 16:25

1 Answers1

4

Well I finally figured out how to solve this. I implemented this answer: https://stackoverflow.com/a/41040630/4441676

Here is the solved code:

Inside ViewController's ViewDidLoad added two observers as below:

// Notification Observers
    NotificationCenter.default.addObserver(self, selector: #selector(ListVC.keyboardWillShow(notification:)), name: Notification.Name.UIKeyboardDidShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ListVC.keyboardWillHide(notification:)), name: Notification.Name.UIKeyboardDidHide, object: nil)

And added related two functions:

@objc func keyboardWillShow(notification: Notification) {
    if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
    }
}

@objc func keyboardWillHide(notification: Notification) {
    UIView.animate(withDuration: 0.2, animations: {
        // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here
        self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    })
}

In the UITableViewCell where UITextFields are connected, simply implement UITextField Delegate and add related Notification.posts when keyboard will show/hide:

deciPadField.delegate = self

func textFieldDidBeginEditing(_ textField: UITextField) {
    NotificationCenter.default.post(name: Notification.Name.UIKeyboardDidShow, object: self)
}

func textFieldDidEndEditing(_ textField: UITextField) {
    NotificationCenter.default.post(name: Notification.Name.UIKeyboardDidHide, object: self)
}

I have inputAccessoryView, so dismissing keyboard handled via following:

// Decipad config for adding Done button above itself
let toolBar = UIToolbar()
toolBar.sizeToFit()
let flexiableSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(self.doneClicked))
toolBar.setItems([flexiableSpace, doneButton], animated: false)

@objc func doneClicked(){
    updateTotalWorth()
    deciPadField.resignFirstResponder()
    deciPadField.endEditing(true)
}

Hope this would help for someone else.

Vetuka
  • 1,078
  • 1
  • 14
  • 34