Search code examples
ioscocoa-touchuikituisearchdisplaycontroller

UISearchDisplayController: Cancel as dismiss button


I have this problem with UISearchDisplayController. I want to use it in a dedicated view for handling searches. For my controller class I used a composition of the searchbar, the searchcontroller and a tableviewcontroller. SearchViewController is linked to a .xib.

@interface SearchViewController : UIViewController <UISearchBarDelegate, UISearchDisplayDelegate, UITableViewDelegate, UITableViewDataSource> {
  NSSet *words;
  UISearchDisplayController *searchController;
  UISearchBar *searchBar;
  PhraseTableViewController *phraseTableViewController;
}

As soon as SearchViewController appears, the searchbar becomes firstresponder and the search is therefore active. Everything looks and works fine, with one exception: In order to get back to the previous view, I want to use the cancel button of the SearchBar. Now here comes the problem: If the user touches the grey area of the underlying tableview when no search has been made, the cancel button (and therefore the only way to get back) disappears.

Here's the thing I've tried to solve this:

  1. showsCancelButton = YES on searchDisplayControllerDidEndSearch. Works, but the resulting UI is ugly as hell: The cancel button transitions out and then reappears.
  2. Add a UITapGestureRecognizer to the searchcontroller's searchResultTableView in the hope to catch the tap. Doesn't work, it still goes through.
  3. Subclass UISearchBar and override self.searchController.searchBar setShowsCancelButton: - Didn't work.

Ideas how to solve this:

  1. Always hide the cancel button and instead add a custom button. Question: Do you have an idea how to do this such that the UI looks good? What concerns me most is to handle all the cases such that the search-textfield always keeps nicely at the side of the button.
  2. Ditch UISearchDisplayController all together for a more flexible solution. Do you know where to look? Pre-iOS 3 this controller didn't exist, right? Aren't there solutions around that still work?

I'm glad for any input of yours. Thanks in advance.


Solution

  • Ok I think I've found a way that works: I just unhide the navigation bar for the right kind of event such that the user gets a back button. See the relevant code below, however I must add that some of the event handling is part of the solution for other "special" behaviors of UISearchDisplayController that make the navigation bar reappear at the wrong times (before jumping to a detail page which would also lead to ugly animations).

    Edit: And yes I know it's ugly - but so is that damn UISearchDisplayController if you ask me.

    static BOOL _cancelBtnClicked = NO;
    static BOOL _phraseClicked = NO;
    
    - (void)searchBarCancelButtonClicked:(UISearchBar *)_searchBar{ 
      if(self.searchController.active){
        _cancelBtnClicked = YES;
      }else{
        [self back];
      }
    }
    
    - (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller{
      if(_cancelBtnClicked){
        _cancelBtnClicked = NO;
        [self back];
      }
      else if (!_phraseClicked) {
        [self.navigationController setNavigationBarHidden:NO animated:YES];
      }
    }
    
    - (void)keyboardWillHide:(NSNotification *)notification {
      if (_phraseClicked) {
        _phraseClicked = NO;
      }
      else if(self.isViewLoaded && self.view.window != nil) {
        self.navigationController.navigationBar.hidden = YES;
      }
    }
    
    - (void)hideNavbarAndKeepHidden {        
      self.navigationController.navigationBar.hidden = YES;
      [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];   
    }
    
    -(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      _phraseClicked = YES;
      [self.phraseTableViewController tableView:tableView didSelectRowAtIndexPath:indexPath];
    }