Search code examples
swiftuitableviewresignfirstresponderbecomefirstresponder

TableView viewForHeaderInSection Won't Resume First Responder


Weird situation over here, hoping someone can help. Basically, I have a switch like this:

private var searchMode = false {
    didSet {
        if !searchMode {
            self.tableView.endEditing(true)
        }
        tableView.reloadData()
    }
}

It gets toggled when I hit a button:

@objc func searchTapped() {
    searchMode.toggle()
}

My header is set like this:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    if searchMode {
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "searchHeader") as! SearchTableViewHeader
        header.searchField.becomeFirstResponder()
        header.searchField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)
        return header
    } else {
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "blueHeader") as! OpaqueBlueTableViewHeader
        header.titleLabel.text = "Discover Innovative Services"
        header.subtitleLabel.text = "Learn about our comprehensive range of services"
        return header
    }
}

So far so good. Now here's the thing. The first time I click the button, searchMode becomes true, and my UITextfield appears, and it is correctly the first responder. So far so good. Then when I click it again, the Textfield disappears. So far so good. BUT, when I click it again, the textField correctly appears, but it is so longer the responder! Let me show you the problem. enter image description here

Confusing in the gif, but the first time I click it, it is the first responder, then not again. Anyone know how I can make it resume being first responder?


Solution

  • Okay, these approaches are useful but I discovered here is the only answer that works:

    First, resign the first responder after the textField disappears before calling reloadData()

     private var searchMode = false {
            didSet {
                if let tv = self.tableView.headerView(forSection: 0), let stv = tv as? SearchTableViewHeader {
                    stv.searchField.resignFirstResponder()
                }
                tableView.reloadData()
            }
        }
    

    Then, set first responder at the very last tableView function. This is because there is unexpected behavior is setting first responder in viewForHeader

    func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        if searchMode, let stv = view as? SearchTableViewHeader {
            stv.searchField.becomeFirstResponder()
        }
    }