简体   繁体   中英

Show UISearchController's SearchResultsController on SearchBar Tap

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):

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;
}

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;
}

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
}

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
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM