3

I have UISearchController in the navigationItem.searchController and I want to make it focus when the user selects "Search" from the menu. So shortly, when the user is tapping on the "Search" option in the menu (UITableViewCell) it's getting the view controller that have the searchController in it and calling:

guard let navigationVC = presentingViewController as? UINavigationController else { return }
guard let documentsVC = navigationVC.topViewController as? DocumentsViewController else { return }
documentsVC.searchController.searchBar.becomeFirstResponder()

Then, the UISearchBar is getting focus, the keyboard is appearing and then it's immediately disappearing, and I don't have any code that would make it disappear (like view.endEditing()).

1 GIF is worth more than 1,000 words: Keyboard is appearing and immediately disappearing when UISearchBar.becomeFirstResponder() is getting called

Ido
  • 473
  • 4
  • 16
  • Can you try setting the `isActive` property of the `searchController` to `true` before calling `documentsVC.searchController.searchBar.becomeFirstResponder()`? Also, make sure that `documentsVC.searchController.searchBar.becomeFirstResponder()` is called on the main thread. – Mohammed Abdullatif Oct 14 '18 at 02:07
  • Tried that also, didn't work: ```documentsVC.searchController.isActive = true DispatchQueue.main.async { documentsVC.searchController.searchBar.becomeFirstResponder() }``` – Ido Oct 14 '18 at 04:35
  • I'v noticed that when I'm doing that it's working: ```DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { documentsVC.searchController.searchBar.becomeFirstResponder() }``` But I want to do something that wont be lead on specific time, 'Cause right now I'm running this on iPhone X with iOS 12, maybe on other devices with older versions it will need more time. – Ido Oct 14 '18 at 04:42
  • I think this means that the SearchController is still not fully loaded when you call `documentVC.searchController.searchBar.becomeFirstResponder`. Did you try doing the same thing but inside `viewDidAppear` of `documentsViewController`? I hope that this can point you to what could be the problem. – Mohammed Abdullatif Oct 14 '18 at 15:20
  • It's only getting "active", but the keyboard doesn't appears (when I'm calling the code from `viewDidAppear`). This is the issue, I don't know how to detect when the `searchBar` is ready to `becomeFirstResponder`. – Ido Oct 14 '18 at 19:36
  • Can you check if the UISearchController delegate method `didPresentSearchController'` is called after you set `documentsVC.searchController.isActive = true` in `viewDidAppear`? I think inside `didPresentSearchController` delegate method you can call `documentsVC.searchController.searchBar.becomeFirstResponder()`. The search controller should be fully loaded by that time. I found also [this thread](https://stackoverflow.com/questions/27951965/cannot-set-searchbar-as-firstresponder/28527114) that maybe helpful for you. – Mohammed Abdullatif Oct 15 '18 at 11:07
  • Also in `didPresentSearchController`, `documentsVC.searchController.searchBar.canBecomeFirstResponder` is `false` so I can't use `documentsVC.searchController.searchBar.becomeFirstResponder()`. – Ido Oct 15 '18 at 20:13
  • I'v posted some workaround for this problem, if you have some better way, please feel free to post answer with it. – Ido Oct 15 '18 at 21:16

2 Answers2

2

So, after many tries I got some way to make it work, but I'm sure there is a much more elegant ways to do this, so if someone think that they have better way, please post it here and I may use it and mark your answer as the correct one.

Create the function focusOnSearchBar() in YourViewController:

func focusOnSearchBar() {
  let searchBar = searchController.searchBar
  if searchBar.canBecomeFirstResponder {
    DispatchQueue.main.async {
      searchBar.becomeFirstResponder()
    }
  } else {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
      self.focusOnSearchBar()
    }
  }
}

What it actually do is use itself recursively and check (every 0.1 sec) if searchBar.canBecomeFirstResponder. This is the problematic/not elegant thing.

Then, add this to viewDidAppear():

if focusOnSearch {
  searchController.isActive = true
}

Don't forget to add extension to your ViewController for UISearchControllerDelegate (and of course, set searchController.delegate = self) and implement didPresentSearchController (that will be invoke by setting searchController.isActive = true):

extension YourViewController: UISearchControllerDelegate {
  func didPresentSearchController(_ searchController: UISearchController) {
    if focusOnSearch {
      focusOnSearchBar()
    }
  }
}

Now all you have to do is to set focusOnSearch = true in the prepare(for segue:sender:).

*Note: if you want to focusOnSearchBar while you are in the same viewController of the searchBar, just set:

 focusOnSearch = true
 searchController.isActive = true

And it will work by itself.

Ido
  • 473
  • 4
  • 16
0

Make your searchbar first responder in the viewDidLoad method. That will make sure everything is ready before focusing the search bar.

HAK
  • 2,023
  • 19
  • 26
  • When I'm calling it in `viewDidLoad()` it doesn't even focus on the `searchBar` like it did in the GIF, nothing happening. – Ido Oct 13 '18 at 20:27