66

Whenever a user begins editing a UISearchDisplayController's search bar, the search controller becomes active and hides the view's navigation bar while presenting the search table view. Is it possible to prevent a UISearchDisplayController from hiding the navigation bar without reimplementing it?

hadronzoo
  • 1,696
  • 1
  • 15
  • 17

15 Answers15

60

I just debugged a bit into UISearchDisplayController and found that it's calling a private method on UINavigationController to hide the navigation bar. This happens in -setActive:animated:. If you subclass UISearchDisplayController and overwrite this method with the following code you can prevent the navigationBar from being hidden by faking it to be already hidden.

- (void)setActive:(BOOL)visible animated:(BOOL)animated;
{
    if(self.active == visible) return;
    [self.searchContentsController.navigationController setNavigationBarHidden:YES animated:NO];
    [super setActive:visible animated:animated];
    [self.searchContentsController.navigationController setNavigationBarHidden:NO animated:NO];
    if (visible) {
        [self.searchBar becomeFirstResponder];
    } else {
        [self.searchBar resignFirstResponder];
    }   
}

Let me know if this works for you. I also hope this won't break in future iOS versions... Tested on iOS 4.0 only.

stigi
  • 6,661
  • 3
  • 40
  • 50
  • 2
    How did you do figure this out? I would like to change the behavior of the search, so that it keeps the table view dimmed until the user clicks on the search button, and your debugging solutions seems like a really elegant way to achieve this. – Jan Aagaard Dec 14 '10 at 22:51
  • 1
    Great, this is the best answer! – Míng Sep 17 '11 at 07:35
  • This is preventing some things to work correctly (like search scope or editable tableview) – Sylverb Oct 15 '12 at 01:48
  • 1
    Definitively this is the solution – Teofilo Israel Vizcaino Rodrig Aug 21 '13 at 15:08
  • 1
    this should be the answer. accepted answer doesn't fix the bug when you have a detail view. – Irina Oct 09 '13 at 18:06
  • 1
    USE THIS ANSWER! This is fully functional, and Does Not have the detail view bug that the accepted answer has. Tested working in iOS 6/7. – Fateh Khalsa Oct 28 '13 at 16:51
  • Thanks guys for the responses. Let's ping @hadronzoo, shall we? :) – stigi Oct 30 '13 at 15:19
  • 2
    Nice answer! But i had to add self.searchContentsController.edgesForExtendedLayout = UIRectEdgeBottom; for iOS 7 –  Aug 18 '14 at 13:54
  • @SebastianKeller yes, actually the answer is from 2010. That's way before iOS 7 & `edgesForExtendedLayout` :) – stigi Aug 19 '14 at 16:35
  • 2
    @stigi: i know the answer is some years old, I just wanted to leave a hint if somebody else struggles with this on iOS 7 :) –  Aug 19 '14 at 17:36
  • That doesn't really fix the problem. It does not hide the nav bar but if you have some scope bars below, then trying to tap on a scope bar makes search display controller resign first responder (scope bars now are bellow black semi-dimmed view for hiding the tableview behind...). Must find way to layout all views again... :( – Giorgos Ath Aug 22 '14 at 09:26
52

The simplest solution and no hacks.

@interface MySearchDisplayController : UISearchDisplayController

@end

@implementation MySearchDisplayController

- (void)setActive:(BOOL)visible animated:(BOOL)animated
{
    [super setActive: visible animated: animated];

    [self.searchContentsController.navigationController setNavigationBarHidden: NO animated: NO];
}

@end
Pavel Sharanda
  • 919
  • 10
  • 9
  • 9
    Agreed, this is the simplest solution. Just for completion: if you're using a xib, then select the search display controller, tap the identity inspector and change the class to MySearchDisplayController. – Adriana Nov 05 '12 at 15:57
  • 5
    Have you tried navigating to detail controller from results and back? This breaks views layout for me. iOS 6.1 – paiv Feb 18 '13 at 15:36
  • not working for me.I've added but not found searchContentsController – Gajendra K Chauhan Jun 18 '13 at 09:17
  • this doesn't work when you have a detail view you want to show. once clicks back from the detail view the search bar slides under the navbar. – Irina Oct 09 '13 at 18:06
  • Anyone else get this exception: [<__NSArrayI 0xa6b82a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key colorMatrix. I'm using this on iPhone 7.0.3. @stigi's solution throws this as well. – mkko Jan 14 '14 at 20:41
  • @mkko in iOS7, there's a property that you can set on `UISearchDisplayController` called `displaysSearchBarInNavigationBar` – jakenberg Jan 21 '14 at 23:05
  • @jsksma2 Thanks, but I know of it and it's not really serving my purpose. `displaysSearchBarInNavigationBar` replaces the toolbar with its own class and I couldn't find a way to replace the title temporarily with a label. I was looking for a toolbar similar to Apple Maps and had to make one of my own (a `UIView` with buttons mimicing bar button items). – mkko Jan 22 '14 at 19:17
  • Thank goodness for this post. I looked through several to no avail. This worked fantastic to prevent iOS 7 from janking up my UISearchDisplayController and UISearchBar animations. – Matt Hudson Mar 07 '14 at 02:23
  • I subclassed UISearchDisplayController and set the subclass from storyboard. But the overridden method never gets called. – Saleh Masum Sep 09 '14 at 12:33
  • 1
    This is the right answer, but with the stigi code (below). – Javier Giovannini Oct 29 '14 at 21:11
52

The new UISearchController class introduced with iOS 8 has a property hidesNavigationBarDuringPresentation which you can set to false if you want to keep the navigation bar visible (by default it will still be hidden).

René
  • 2,912
  • 1
  • 28
  • 46
27

The above answers didn't work quite right for me. My solution is to fool the UISearchDisplayController into thinking there wasn't a UINavigationController.

In your view controller, add this method

- (UINavigationController *)navigationController {
    return nil;
}

This had no untoward side effects for me, despite seeming like a really bad idea... If you need to get at the navigation controller, use [super navigationController].

joerick
  • 16,078
  • 4
  • 53
  • 57
  • 1
    I read all the proposed solutions and this was by far the simplest and the best. – malhal Jun 30 '12 at 10:41
  • I got: *** Assertion failure in -[UISearchDisplayController setActive:animated:] when hitting the cancel button. – richy Jul 20 '12 at 06:26
  • Fixed this by calling [self.searchDisplayController setActive:NO animated:NO]; in - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar – richy Jul 20 '12 at 06:48
  • Hmm. It's possible this no longer works with current iOS versions. – joerick Jul 20 '12 at 08:05
  • 1
    This worked for me with iOS 7 on the iPad. However, I added some code that makes it only return nil when the search display controller is calling it & it's on the ipad, otherwise it behaves normally. http://pastebin.com/6pJ2WWsy – Mike Sprague Dec 18 '13 at 17:41
  • I still need to push. [self.navigationController pushViewController:[iFeedbackFormStyle2VC loadFromNib] animated:YES]; But if it is empty, i can't push. how shall i do? – Khant Thu Linn Apr 18 '16 at 08:42
  • 2
    @KhantThuLinn use `[super navigationController]` instead of `self.navigationController` – joerick Apr 18 '16 at 13:28
  • Can't decide if I'm amazed or horrified, but this worked. The stigi answer was close, but when I tapped into the search bar, the thing would still slide up BEHIND my nav bar (i.e. I could type and have the search "work", but I couldn't see the text). – ghostatron Sep 13 '16 at 23:08
10

Since iOS 8.0 the same behavior can be achieved by setting the UISearchController's self.searchController.hidesNavigationBarDuringPresentation property to false.

The code in Swift looks like this:

searchController.hidesNavigationBarDuringPresentation = false
Goppinath
  • 10,569
  • 4
  • 22
  • 45
Peter Ivanics
  • 518
  • 8
  • 13
9

Tried this a different way, without subclassing UISearchDisplayController. In your UIViewController class where you set the delegate for UISearchDisplayController, implement searchDisplayControllerDidBeginSearch: and add use

[self.navigationController setNavigationBarHidden:NO animated:YES];

Did the trick for me, hope that helps.

James Bedford
  • 28,702
  • 8
  • 57
  • 64
ah335
  • 115
  • 1
  • 1
4

I ran into this while tackling a slightly different problem. While using UISearchDisplayController, I want the search bar to be in the navigation bar (not under).

It's not hard to put the search bar in the navigation bar (see UISearchBar and UINavigationItem). However, UISearchDisplayController assumes the search bar is always underneath the navigation bar and (as discussed here) insists on hiding the navigation bar when entering search, so things look awful. Additionally, UISearchDisplayController tints the search bar lighter than normal.

I found a solution. The trick is to (counter-intuitively) unhook UISearchDisplayController from controlling any UISearchBar at all. If using xibs, this means deleting the search bar instance, or at least unhooking the outlet. Then create your own UISearchBar:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UISearchBar *searchBar = [[[UISearchBar alloc] init] autorelease];
    [searchBar sizeToFit]; // standard size
    searchBar.delegate = self;

    // Add search bar to navigation bar
    self.navigationItem.titleView = searchBar;
}

You will need to manually activate the search display controller when the user taps the search bar (in -searchBarShouldBeginEditing:) and manually dismiss the search bar when the user ends searching (in -searchDisplayControllerWillEndSearch:).

#pragma mark <UISearchBarDelegate>

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
    // Manually activate search mode
    // Use animated=NO so we'll be able to immediately un-hide it again
    [self.searchDisplayController setActive:YES animated:NO];

    // Hand over control to UISearchDisplayController during the search
    searchBar.delegate = (id <UISearchBarDelegate>)self.searchDisplayController;

    return YES;
}

#pragma mark <UISearchDisplayDelegate>

- (void) searchDisplayControllerDidBeginSearch:(UISearchDisplayController
*)controller {
    // Un-hide the navigation bar that UISearchDisplayController hid
    [self.navigationController setNavigationBarHidden:NO animated:NO];
}

- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController
*)controller {
    UISearchBar *searchBar = (UISearchBar *)self.navigationItem.titleView;

    // Manually resign search mode
    [searchBar resignFirstResponder];

    // Take back control of the search bar
    searchBar.delegate = self;
}
Community
  • 1
  • 1
jrc
  • 20,354
  • 10
  • 69
  • 64
3

Really nice solution, but it was crashing my app under iOS6. I had to make the following modification to get it work.

@implementation ICSearchDisplayController

    - (void)setActive:(BOOL)visible animated:(BOOL)animated
    {
        if (visible == YES) {
            [super setActive:visible animated:animated];
            [self.searchContentsController.navigationController setNavigationBarHidden:NO animated:NO];
        } else {
            [super setActive:NO animated:NO];
        }
    }
3

This seem to solve it for me. Tested in both iOS5/6.1. No visual issues that I could see.

- (void)viewDidAppear
{
    [super viewDidAppear];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillAppear:) name:UIKeyboardWillShowNotification object:nil];
}

-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)keyboardWillAppear:(NSNotification *)notification
{
    [self.navigationController setNavigationBarHidden:NO animated:NO];
}

-(void)viewDidLayoutSubviews{
    [self.navigationController setNavigationBarHidden:NO animated:NO];
}
Daniel Ryan
  • 6,976
  • 5
  • 45
  • 62
  • worked for me by just adding part of this:-(void)viewDidLayoutSubviews { [self.navigationController setNavigationBarHidden:NO animated:NO]; // don't hide navigation bar when user taps the search bar } – Richie Hyatt Jul 30 '14 at 22:27
  • I was having a similar problem, my navigation bar was disappearing when the keyboard appeared. Using the last 2 methods of the above code fixed it. – avance Sep 03 '15 at 16:26
3

iOS 7 screws things up a bit... for me this worked perfectly:

/**
 *  Overwrite the `setActive:animated:` method to make sure the UINavigationBar 
 *  does not get hidden and the SearchBar does not add space for the statusbar height.
 *
 *  @param visible   `YES` to display the search interface if it is not already displayed; NO to hide the search interface if it is currently displayed.
 *  @param animated  `YES` to use animation for a change in visible state, otherwise NO.
 */
- (void)setActive:(BOOL)visible animated:(BOOL)animated
{
    [[UIApplication sharedApplication] setStatusBarHidden:YES];
    [self.searchContentsController.navigationController setNavigationBarHidden:YES animated:NO];

    [super setActive:visible animated:animated];

    [self.searchContentsController.navigationController setNavigationBarHidden:NO animated:NO];
    [[UIApplication sharedApplication] setStatusBarHidden:NO];
}

The reason for show/hide the statusbar

Paul Peelen
  • 10,073
  • 15
  • 85
  • 168
1

I think the best solution is to implement the UISearchDisplayController yourself.

It's not that difficult. You only need to implement UISearchBarDelegate for your UIViewController and include a UITableView to display your search results.

frankli
  • 138
  • 5
0

@Pavel's works perfectly well. However, I was trying to get this into a UIPopoverController and the text in the field gets pushed slightly when the search bar's text field becomes the first responder, and that looks a bit ugly, so I fixed it by calling the super method with animated set to NO.

XCool
  • 976
  • 11
  • 32
0

As jrc pointed out "unhook UISearchDisplayController from controlling any UISearchBar" seems to work for me. If I pass nil as a parameter when creating UISearchDisplayController the navigation bar stays visible at all times:

searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:nil contentsController:self];
Dasha Salo
  • 5,159
  • 5
  • 26
  • 28
0

I was adding custom navigation bar on my ViewController which was getting hidden on search, a quick but not so good fix was

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
    [self.view addSubview:_navBar];
}

_navBar is UINavigationBar added programmatically, doing this helped me navigation bar from hiding.

sahiljain
  • 2,215
  • 1
  • 29
  • 39
0

Just wanted to add to stigi answer. When you cancel search and start search again - search results table won't be react to touches so you need to add next line

self.searchResultsTableView.alpha = 1;

So updated code looks next way

 - (void)setActive:(BOOL)visible animated:(BOOL)animated;
 {
    if(self.active == visible) return;
    if (visible) {
        [self.searchContentsController.navigationController setNavigationBarHidden:YES animated:NO];
        [super setActive:visible animated:animated];
        [self.searchContentsController.navigationController setNavigationBarHidden:NO animated:NO];
        self.searchResultsTableView.alpha = 1;
        [self.searchBar becomeFirstResponder];
    } else {
        [super setActive:visible animated:animated];
        [self.searchBar resignFirstResponder];
    }
}
alex1704
  • 479
  • 5
  • 8