How do I make a navigation bar stay visible with UISearchController? - ios

So I have a navigation controller hooked up to a view controller. That obviously provides the default navigation bar. Below that, I have another navigation bar with two buttons. In code I am manually adding the search bar that the UISearchController provides to that navigation bar. Below that, there is another filter view (custom), then a UITableView. Everything seems to work, but when I click search and start typing, the result view covers up the second navigation bar and the search bar, making it sort of useless. I tried playing around with self.searchController.hidesNavigationBarDuringPresentation = NO; but that just hides the main navigation bar. The even more infuriating part is the result view controller is actually leaving space for the second navigation bar, but it's just hidden behind this. It is even possible to see this in the debug view heirachy menu. Here’s the code I'm using for the search controller:
CUSearchResultsTableViewController *results = [self.storyboard instantiateViewControllerWithIdentifier:#"searchResults"];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:results];
self.searchController.searchResultsUpdater = self;
self.searchBarNavItem.titleView = self.searchController.searchBar;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.obscuresBackgroundDuringPresentation = NO;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.definesPresentationContext = YES;
self.searchController.delegate = self;
self.searchController.searchBar.delegate = self;

Here is how you can do it:
objective-c
[self.navigationItem setHidesSearchBarWhenScrolling:NO];
Swift
self.navigationItem.hidesSearchBarWhenScrolling = false;

Related

How to prevent the UISearchController from showing the navigation bar?

My use case is kind of strange. I'm using my own navigation bar, so I hide the default one with
[self.navigationController setNavigationBarHidden:YES animated:NO];
on viewWillAppear.
Everything works great, but if I have the keyboard open on an active search, and then I go back to a previous UIViewController, then the native navigation bar shows up again, and I end up with 2 navigation bars (my own and the default).
This is how I'm setting up my UISearchController in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.obscuresBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
[self.searchController.searchBar sizeToFit];
self.definesPresentationContext = YES;
self.searchController.hidesNavigationBarDuringPresentation = YES;
self.searchController.searchBar.searchBarStyle = UISearchBarStyleMinimal;
}
I already tried removing the self.definesPresentationContext = YES; (or turning it to NO), but that creates a different issue, which is that when I go back to the previous UIViewController the search bar stays on top of everything else! Until I tap on Cancel. I also tried calling the Cancel button programatically on viewWillDissappear, but that didn't work either...
So I'm running out of options, and that's why I'm here.
Any thoughts?
Using a navigation bar that is not the standard maybe it's not the best idea. (I know customers can sometimes be stubborn, but we should teach them that sometimes standard solutions have a lot of good points, like low maintenance for example, which turns in lower bills for them).
Having said that, as a last resort I may suggest you a quite "strong" approach.
You could subclass the UINavigationController with a custom class, and inside this class you could override the setNavigationBarHidden method like this
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated{
[super setNavigationBarHidden:YES animated:NO];
}
method. This should make the bar hidden all the time.
Still, i'm not a big supported of this kind of solutions, but it may work in your case.

Navigating to a view (push) from a search results table (UISearchController) and keeping the search results in place

I have a search results table (UISearchController) that is presented on a view that exists on a navigation stack. I present the search controller and table on my view like this:
_searchResultsTableViewController = [[CalendarSearchResultsTableViewController alloc] init];
_searchResultsTableViewController.delegate = self;
_searchController = [[UISearchController alloc] initWithSearchResultsController:_searchResultsTableViewController];
_searchController.delegate = self;
_searchController.hidesNavigationBarDuringPresentation = NO;
_searchController.dimsBackgroundDuringPresentation = YES;
_searchController.obscuresBackgroundDuringPresentation = YES;
_searchController.active = YES;
_searchController.searchBar.delegate = self;
UIEdgeInsets adjustForTabbarInsets = UIEdgeInsetsMake(0, 0, CGRectGetHeight(self.tabBarController.tabBar.frame), 0);
_searchResultsTableViewController.tableView.contentInset = adjustForTabbarInsets;
_searchResultsTableViewController.tableView.scrollIndicatorInsets = adjustForTabbarInsets;
[self.navigationController presentViewController:_searchController animated:YES completion:nil];
This presents a UISearchBar over the UINavigationBar and the search table appears as expected. When the user selects a search result from the table, I want to present a view for the selection by pushing it onto the navigation stack while the search results remain in place. The way I have it working now is that the view that presented the search controller opens the view, but this requires the search controller to be dismissed because the search result view is presented behind the search results table.
How can I make it so that the search controller pushes the search result view onto the navigation stack, allowing the user to navigate back to the search results?
An example of how I expect this to work can be found in the iOS Mail of Calendar app (when you search).
My navigationController property in the search results controller is nil, so I cannot currently present the search result view from there.
The project is in both Objective-C and Swift, so answers in either is fine. Any help is much appreciated!
I think there are 3 small changes you need to make.
_searchController.hidesNavigationBarDuringPresentation = YES;
[self presentViewController:_searchController animated:YES completion:nil];
self.definesPresentationContext = YES; // in viewDidLoad of the viewController that presented the searchController

Can't edit UISearchController UISearchBar in navigationItem titleView

When setting a UISearchController search bar in the navigationItem titleView, the search bar can't be edited.
In my viewDidLoad I am configuring a UISearchController.
self.searchViewController = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SearchViewController class])];
self.searchViewController.delegate = self;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchViewController];
self.searchController.delegate = self;
self.searchController.searchResultsUpdater = self;
self.searchController.searchBar.delegate = self;
self.navigationItem.titleView = self.searchController.searchBar;
I can't tap the search bar. The cursor does not appear and there is no user interaction.
Oddly, if I initialize the UISearchController locally without setting it to a property, then I can edit the search bar, just no delegate callbacks.
self.searchViewController = [self.storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([SearchViewController class])];
self.searchViewController.delegate = self;
UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchViewController];
searchController.delegate = self;
searchController.searchResultsUpdater = self;
searchController.searchBar.delegate = self;
self.navigationItem.titleView = searchController.searchBar;
Another interesting behavior is that the clear button works (if some text is set in the search bar while initializing).
I had the same issue.
Imagine you have FirstViewController and SecondViewController, and booth have a UISearchBar on the titleView.
To fix the problem I had this code to booth UIViewController's.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.definesPresentationContext = true
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.definesPresentationContext = false
}
I am setting self.definesPresentationContext = YES; in the view controller that presents the view controller in question.
This must be set to self.definesPresentationContext = NO; in viewWillAppear:.
Now the search bar in the presented view controller can be edited.
Set your search bar to navigation title view :
self.navigationItem.titleView = self.searchBarTop;
then just set this view either left/right button of Navigation Bar
UIBarButtonItem *searchBarItem = [[UIBarButtonItem alloc] initWithCustomView:searchBar];
self.navigationItem.rightBarButtonItem = searchBarItem;
I hope this will work for You!!
I have just solved a very similar problem in my app, and thought I'd share the solution in case the existing solutions don't fix it for you.
In my case I had a tab bar application, with my custom controllers in the tabs, embedded in the navigation controllers. The symptoms were exactly the same, the search bar showed in the title area of the navigation bar, but was not interactive.
I have discovered that the problem was that I used my custom subclass of the UITabBarController and I have overriden the viewWillAppear(animated:) method, but forgot to call super.viewWillAppear(animated:) in the implementation. The additional symptom was that when I switched the tabs, the search bar suddenly became interactive and everything worked fine, just the interaction on the initial tab was disabled.
I hope this helps someone.
In your first block of code, you're instantiating with a SearchViewController identifier. In the second, you're using HBSearchViewController. This suggests that there might be another difference in your code besides using / not using an outlet.

UISearchController changing status bar color on invocation

I have the following code in my app, specifically in viewDidLoad: that sets up my UISearchController.
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.definesPresentationContext = NO;
self.searchController.searchBar.scopeButtonTitles = #[];
self.searchController.searchBar.searchBarStyle = UISearchBarStyleProminent;
[_tableView setTableHeaderView:_searchController.searchBar];
Whenever the search bar (which is added to the tableView) is invoked, the UIStatusBar color changes from UIStatusBarStyleLightContent to dark (white to black). Now, I figured out if I set,
self.definesPresentationContext = NO;
to the following:
self.definesPresentationContext = YES;
the issue is solved and the UIStatusBar color is preserved. However, another issue arises. When self.definesPresentationContext is set to YES
, upon invocation the search bar shifts down for some reason, coincidently (or rightfully so) right under where the bottom of the UIRefreshControl displays on the tableView.
Setting View-controller based status bar appearance to No is not a solution if you want the view controllers to define how the status bar looks.
My solution consisted of two things:
Make sure the presenting view controller has definesPresentationContext set to YES
Make sure both the view controller that is pushed and the pushing view controller are laid out beneath the navigation bar (set extendedLayoutIncludesOpaqueBars to YES)
As of iOS 10 (maybe earlier?), if you have "View controller-based status bar appearance" set to YES in your Info.plist, simply set the preferredStatusBarStyle in the UIViewController that the UISearchController is included in.
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
(you don't need to subclass or create a category/extension of UISearchController to override preferredStatusBarStyle... it uses the preferredStatusBarStyle that you set in your UIViewController)
I needed full control over my status bar colour. I use the extensions found here to ensure that the visible view controller is setting the preferred status bar colour.
For me it was therefore necessary to override UISearchController and override preferredStatusBarStyle and return the style I wanted.
If you ViewController is inside a TabBarController then -
Instead of
self.definesPresentationContext = YES;
Use self.tabBarController.definesPresentationContext = YES;
This worked for me in above scenario.
The status bar that is displayed when the search controller is presented (is active) belongs to the search controller. To set the preferred status bar style you must add a category to UISearchController and then override the preferredStatusBarStyle method.
Below is an example of the implementation file of the category:
#implementation UISearchController (Customization)
-(UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
#end
Or we can write an extension on Swift (version 2, but you can translate it to 3 easily):
extension UISearchController {
override public func preferredStatusBarStyle() -> UIStatusBarStyle{
if Theme.lightTheme() {
return UIStatusBarStyle.Default
}
else {
return UIStatusBarStyle.LightContent
}
}
}
Where Theme is a class that regulate app colour theme.

Search Results Visibly Scrolling Underneath the Status Bar

When performing a search on a dataset via a UISearchBar, the search results successfully display in the UITableViewController's UITableView. However, when scrolling down through the results, the UITableView's rows visibly appear underneath the UINavigationBar and the simulator's status bar.
This obviously is not the look that I'm going for. Ideally, I would like the UISearchBar to act as the UITableView's header with all search results being contained below the UISearchBar's scope buttons, but my attempts have been unsuccessful.
Below is the Storyboard setup of the relevant UITableViewController and its UITableView's properties.
Below is the relevant code that I am using to setup the UISearchController and its UISearchBar.
BallotTunesSearchTableViewController.h
#interface BallotTunesSearchTableViewController : UITableViewController <UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate>
BallotTunesSearchTableViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [[UIApplication sharedApplication] delegate];
// Initialize the search controller
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
// Setup the search bar
self.searchController.searchBar.delegate = self;
self.searchController.searchBar.scopeButtonTitles = [NSMutableArray arrayWithObjects:SongScopeName, ArtistScopeName, AlbumScopeName, nil];
self.tableView.tableHeaderView = self.searchController.searchBar;
}
Update: Note that the UITableViewController is embedded in a UINavigationController, and when setting the translucence of the UINavigationBar to NO, the UISearchBar slides off the view along with the UINavigationBar.
Also note that I am not implementing the UISearchBar in Storyboard (however, I may take that route if I can't get my current setup to work).
After several face palms, it all came down to the lack of this line of code:
self.definesPresentationContext = YES;
Setting the presentation context to YES indicates that the view controller's view should be covered when the view controller presents the UISearchController.

Resources