-1

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?

AlexK
  • 336
  • 8
  • 21
  • 41
  • May be this help: https://stackoverflow.com/questions/30445823/programmatically-place-the-cursor-inside-a-textfield-of-a-custom-tableview-heade – Kishan Bhatiya Jan 21 '20 at 05:26
  • I tried this approach. It let's me resign the responder fine, but it doesn't resume the first responder.. :( – AlexK Jan 21 '20 at 05:54

3 Answers3

1

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()
    }
}
AlexK
  • 336
  • 8
  • 21
  • 41
0

Try:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {


if searchMode {



} else {
    let previousHeader = tableView.dequeueReusableHeaderFooterView(withIdentifier: "searchHeader") as! SearchTableViewHeader
  previousHeader.searchField.resignFirstResponder()

   }
 }
Celeste
  • 1,519
  • 6
  • 19
0

Here are two things you may try:

  • Call tableView.layoutIfNeeded() after the reloadData() call and make the header view the first responder after that:
if !searchMode {
    self.tableView.endEditing(true)
}
tableView.reloadData()
tableView.layoutIfNeeded()
if let headerView = tableView.headerView(forSection: 0) as? SearchTableViewHeader {
    header.searchField.becomeFirstResponder()
}
  • Implement tableView.reloadDataWithCompletion as suggested here and call makeFirstResponder in the completion block:
if !searchMode {
    self.tableView.endEditing(true)
}
tableView.reloadData() {
    if let headerView = tableView.headerView(forSection: 0) as? SearchTableViewHeader {
        header.searchField.becomeFirstResponder()
    } 
}
Werner Altewischer
  • 10,080
  • 4
  • 53
  • 60