I am using UISearchController not UISearchDisplayController, and I want to show SearchResultController on SearchBar Tap right away. Right now it's showing like this (when I tap on the search bar):
5 Answers
When results are empty, UISearchController
's viewController
is still hidden. That's why we have to fiddle our way around using UISearchControllerDelegate
's willPresentSearchController:
After initializing self.searchController
make your ViewController conform to `UISearchControllerDelegate:
self.searchController.delegate = self;
Implement willPresentSearchController:
in your ViewController:
- (void)willPresentSearchController:(UISearchController *)searchController
{
dispatch_async(dispatch_get_main_queue(), ^{
searchController.searchResultsController.view.hidden = NO;
});
}
The async dispatch is necessary, because otherwise it will be overridden by the internal behavior. You could go fancy here and use an animation to have the table view fade in.
Additionally, implement didPresentSearchController:
for sanity:
- (void)didPresentSearchController:(UISearchController *)searchController
{
searchController.searchResultsController.view.hidden = NO;
}

- 2,279
- 1
- 23
- 31
-
it works only for the first time when tap anything from the Search Results Controller when you again tap Search Bar it doesn't show the Search Results Controller. – Attiqe Aug 04 '15 at 15:45
-
Did you try implementing the counter delegate methods `didDismissSearchController:` and `willDismissSearchController:`? Set inside of those methods `searchController.searchResultsController.view.hidden = YES;`. – bhr Aug 04 '15 at 20:01
-
I did but it didn't have any effect on it. – Attiqe Aug 05 '15 at 19:04
-
I cannot reproduce it with the sample code (https://developer.apple.com/library/ios/samplecode/TableSearch_UISearchController/Introduction/Intro.html). Are the delegate methods being called when you tap the second time? – bhr Aug 05 '15 at 23:14
-
willPresentSearchController and didPresentSearchController does't get called second time and willDismissSearchController & didDismissSearchController only gets called when I tap Cancel button of the Search Bar. I have developed a repos on [github](https://github.com/attiqeurrehman/UISearchController) that reproduces the issue. – Attiqe Aug 09 '15 at 08:30
-
Use self.searchController.active = NO;` instead of `[self.searchController.searchBar resignFirstResponder];` when calling `didShowFavourite`. There's also no need to implement the delegate methods for dismissal. – bhr Aug 09 '15 at 17:18
-
it worked, please update the answer as well so others can benefit from it as well. – Attiqe Aug 10 '15 at 07:23
-
2@tubtub This works great for the first launch. If we type something and then remove the entered text completely, the resultsController gets hidden again. Is there a way to solve this too? – sdtaheri Aug 24 '15 at 16:07
I found that the other answers had flickering due to using dispatch_async
. They used this so that their changes would be applied after the internal behavior of the search controller had completed, but this leaves a couple frames where the internal behavior is applied before it is overridden. Using KVO allowed me to immediately override the internal behavior without any flickering.
I also found that the other answers did not keep the search results controller visible when a user tapped the ⓧ button to clear the contents of the search bar, which seems incorrect to me.
- (void) viewDidLoad
{
...
self.searchController.delegate = self;
[self.searchController.searchResultsController.view addObserver:self forKeyPath:@"hidden" options:0 context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ( object == self.searchController.searchResultsController.view &&
[keyPath isEqualToString:@"hidden"] &&
self.searchController.searchResultsController.view.hidden &&
self.searchController.searchBar.isFirstResponder )
{
self.searchController.searchResultsController.view.hidden = NO;
}
}
- (void) willPresentSearchController:(UISearchController *)searchController
{
searchController.searchResultsController.view.hidden = NO;
}
- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if ( searchText.length == 0 )
self.searchController.searchResultsController.view.hidden = NO;
}
- (void) searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
self.searchController.searchResultsController.view.hidden = YES;
}

- 13,064
- 4
- 46
- 49
-
So far, this has been the most effective answer. The observer is the key (pun mildly intended) – geraldcor Sep 20 '16 at 21:20
-
Thank you for a great solution! But may I ask you why do you handle searchBar: textDidChange method if you already have an observer set up for this purpose? – berec Feb 05 '18 at 10:57
-
It's been a while since I've looked at this, but it looks like the observer only handles the changing "hidden" property, whereas searchBar:textDidChange: detects when the last bit of text is removed, and hides the search results controller. It looks like they're accomplishing two different things. – Chris Vasselli Feb 06 '18 at 14:31
Chris Vasselli's answer is the cleanest way to implement this.
Here it is in Swift 3
override func viewDidLoad() {
super.viewDidLoad()
searchController.delegate = self
self.searchController.searchResultsController?.view.addObserver(self, forKeyPath: "hidden", options: [], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let someView: UIView = object as! UIView? {
if (someView == self.searchController.searchResultsController?.view &&
(keyPath == "hidden") &&
(searchController.searchResultsController?.view.isHidden)! &&
searchController.searchBar.isFirstResponder) {
searchController.searchResultsController?.view.isHidden = false
}
}
}
func willPresentSearchController(_ searchController: UISearchController) {
searchController.searchResultsController?.view.isHidden = false
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if (searchText.characters.count == 0) {
searchController.searchResultsController?.view.isHidden = false
}
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
searchController.searchResultsController?.view.isHidden = true
}

- 91
- 1
- 7
I think this method is better, be careful when searchBar is empty then preload tableview will disappear again.
UISearchBarDelegate
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
dispatch_async(dispatch_get_main_queue()) {
self.searchController.searchResultsController?.view.hidden = false
}
}
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
dispatch_async(dispatch_get_main_queue()) {
self.searchController.searchResultsController?.view.hidden = false
}
}
UISearchControllerDelegate
func willPresentSearchController(searchController: UISearchController) {
dispatch_async(dispatch_get_main_queue()) {
self.searchController.searchResultsController?.view.hidden = false
}
}

- 379
- 3
- 7
Swfit 5 version with simplified code
ModuleBuilder
let resultViewController = YourResultViewController()
let searchBarController = UISearchController(searchResultsController: resultViewController)
searchBarController.searchBar.delegate = resultViewController as? UISearchBarDelegate
navigationItem.searchController = searchBarController
YourResultViewController: UISearchBarDelegate
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty {
DispatchQueue.main.async {
self.view.isHidden = false
}
}
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
DispatchQueue.main.async {
self.view.isHidden = false
}
}

- 351
- 2
- 10