Search code examples
iosswiftuitableviewuitextfielddelegatebecomefirstresponder

After deleteRows used, becomeFirstResponer() doesn't give focus to textField


After any row is deleted by deleteRows(at:with:) from my Table View, I cannot get textfield in a reusable custom cell to get focus by becomeFirstResponder() (which is called after insertRows(at:with:) is called). becomeFirstResponder() is always called within cellForRow(at:).

This problem always starts occuring after a row is deleted from the table view by deleteRows(at:with:). Before any row is deleted there is no problem, becomeFirstResponder() can give focus to the textfield as intended.

I made some tests to understand why this problem occurs after a row is deleted... My tests showed an interesting difference:

Case 1: After any row is deleted (i.e. while becomeFirstResponder() returns false), textFieldShouldBeginEditing(_:) is called BEFORE cellForRow(at:) returns. textFieldDidBeginEditing(_:) is never called.

Case 2: On the other hand, before any row is deleted (i.e. while becomeFirstResponder() returns true), textFieldShouldBeginEditing(_:) is called AFTER cellForRow(at:) returns. textFieldDidBeginEditing(_:) is called immediately after textFieldShouldBeginEditing(_:), as expected.

The problem might be related to this difference, but I couldn't solve this mystery after trying & researching for many hours.

In a related Q&A, it is suggested not to reload tableView from within textFieldDidEndEditing(). In my case, deleteRows(at:with:) method are sometimes called from within textFieldDidEndEditing() and sometimes called from tableView(_:commit:forRowAt:) (i.e. by swiping to left). So this does not explain my problem. Another related Q&A might be this one.

getFocus() in CustomCell's class, called from cellForRow(at:) in TableViewController:

func getFocus() {
    if let itemTextField = itemTextField {
        itemTextField.delegate = self
        itemTextField.isUserInteractionEnabled = true
        itemTextField.becomeFirstResponder()
    }
}

cellForRow(at:):

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let item = items[indexPath.row]
    let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
    cell.setCell(item: item)
    cell.delegate = self

    if insertingRow == true {
        if isTempItem(item: item, indexPath: indexPath) {
                cell.getFocus()
        } else {
            print("isTempItem returned false, no need to give focus.")
        }
    }

    return cell     
}

Solution

  • Don't call getFocus in cellForRowAt, Insteed of when you insert new row, You can scroll to new row and call getFocus in willDisplayCell.

    Hope to help you.