I recently started to rewrite my iOS app to use the new UISearchController and a universal storyboard.
My app is available for both devices (iPhone and iPad) so the change to the universal storyboard using the UISplitViewController was a big advantage.
But sadly the UISearchController isn't working as expected. I added the UISearchController with the following lines:
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchBar.sizeToFit()
self.searchController.dimsBackgroundDuringPresentation = false
self.myTableView.tableHeaderView = self.searchController.searchBar
self.searchController.searchResultsUpdater = self
self.definesPresentationContext = true
My controller chain is like that:
UISplitViewController
UITabbarController (Master)
UINavigationController
UITableViewController
UINavigationController (Detail)
UINavigationController
UIViewController
The problem is that in the iPad app the UISearchBar is covered by the UINavigationBar. But if I switch the tabs and go back to the view. The UISearchBar is visible. So somehow after a tabbar switch it redraws the view correctly. In the iPhone Version it works automatically correct.
iPad App
After the first launch the UISearchBar is covered by the UINavigationBar
After switching the tabs the UISearchBar is displayed correctly
iPhone App
The iPhone app is working correctly without changing the tabs..
What I tried:
Using different settings of extendingEdges
Add the SearchController in the viewWillAppear method because I thought I can trick it by adding the search control later on
I assume you setup search controller self.searchController.searchBar.sizeToFit() in viewDidload. Beside that, you need to add this method (Objective-C code):
- (void)viewDidLayoutSubviews {
// you could check
// if (IS_IPAD || (IS_IPHONE_6PLUS && UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)))
[self.searchController.searchBar sizeToFit];
}
In case it does not work, you could add these codes into your viewDidlLoad or viewDidLayoutSubviews:
self.edgesForExtendedLayout = UIRectEdgeNone;
self.yourTableViewController.edgesForExtendedLayout = UIRectEdgeNone;
If you wants to dock your search, please refer my answer here
Related
Looking into setting up a parentViewController with a segmentedControl to switch between two (or more) viewControllers, I found this excellent tutorial: https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/
I then added UITableViewController as one of the childViewControllers.
So far so good.
Then I added a UISearchController and added the searchBar as recommended by Apple:
if #available(iOS 11.0, *) {
self.navigationItem.searchController = searchController
} else {
tableView.tableHeaderView = searchController.searchBar
}
But because there is already a UISegmentedControl in the navigationBar (set up in the storyBoard), the searchBar doesn't show up. I can fall back to the pre iOS 11 method, and the searchBar is now visible in the tableView header, but of course it doesn't have the iOS 11 look anymore.
Is it possible to have both the segmentedControl and searchBar in the navigationBar?
EDIT:
I also tried using the titleView for the segmentedControl, but still, the searchBar does not show up.
One workaround may be to change the appearance of the iOS 10 searchBar, but that would still not feel right.
How can I show a UISearchBar in the NavigationBar?
I can't figure out how to do this.
Your help is very much appreciated.
To put searchBar into the center of navigationBar:
self.navigationItem.titleView = self.searchBarTop;
To put searchBar to the left/right side of navigationBar:
UIBarButtonItem *searchBarItem = [[UIBarButtonItem alloc] initWithCustomView:searchBar];
self.navigationItem.rightBarButtonItem = searchBarItem;
As of iOS 7, the UISearchDisplayController supports this by default. Set the UISearchDisplayController's displaysSearchBarInNavigationBar = YES to get this working easily.
Per the documentation:
Starting in iOS 7.0, you can use a search display controller with a navigation bar (an instance of the UINavigationBar class) by configuring the search display controller’s displaysSearchBarInNavigationBar and navigationItem properties.
As one commenter noted, using searchDisplayController.displaysSearchBarInNavigationBar = true ends up hiding any existing left/right bar button items.
I've found two different ways of adding a searchBar to a navigationBar using iOS7's new property on searchDisplayController.
1) Nib Based Approach
If you're using a .xib, you can set a User Defined Runtime Attribute for this value and for whatever reason, the leftBarButtonItem stays in tact. I have not tested it with a rightBarButtonItem.
2) Code (Timing Matters)
If you want to implement in code, timing seems to matter. It seems that you must add the searchBar to the navigationBar first, then set your barButtonItem.
- (void)viewDidLoad
{
...
self.searchDisplayController.displaysSearchBarInNavigationBar = true;
self.navigationItem.leftBarButtonItem = [UIBarButtonItem new];
...
}
Check out Apple's UICatalog sample code. It shows how to use the new UISearchController in three different ways: modally, in nav bar, and below the navigation bar.
Objective C code snippet for UISearchBar in NavigationBar
- (void)viewDidLoad {
UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
if (#available(iOS 11.0, *)) {
self.navigationItem.searchController = searchController;
} else {
self.navigationItem.titleView = searchController.searchBar;
}
}
Read more here
I am adding UISearchController searchBar to the controllers' view like this: self.view.addSubview(searchController.searchBar). The functionality is working perfectly fine except that upon selection of the tableview row the searchbar quickly moves down and reappears from the top. I tried the following things, none of which worked out:
Setting tableView.tableHeaderView = searchController.searchBar instead of directly adding to the view
Adding searchController.searchBar to a separate view that I dragged to the controller setting up constraints on it. Tried clipping to bounds both the newly created view and the searchBar.
Embedding the controller in UINavigationViewController and setting self.navigationItem.titleView = searchController.searchBar. I defined the frame of the searchBar, still nothing.
Tried playing with Extend Edges feature in the storyboard (Under top bars, etc.), but no selection worked out
Adding lines (to viewDIdLoad):
self.extendedLayoutIncludesOpaqueBars = true
self.definesPresentationContext = true
Any help would be greatly appreciated.
If you are using storyboards, you can change it by selecting the view controller and in the attributes inspector deselect Adjust scroll view insets.
After trying all of the suggestion and searching over the internet, it caught my eye that the working examples of the UISearchController implementation are done in UITableViewController, but I had UIViewController with UITableViewDataSource and UITableViewDelegate protocols on it. Unfortunately, due to app architecture I was not able to directly have UITableViewController, so I needed to restructure the app, so that it had UINavigationController where I embeded the searchBar in navigationItem.titleView (and not set it as tableView.tableHeaderView like they always do in various tutorials since I needed the searchBar to be fixed, not hidden when we do scrolling) and it worked. Here is how the ultimate working app architecture looks like:
The TrainingContainerViewController has two Container Views, in one we embed TrainingFilterTableViewController that shows up the ultimate results of the autocomplete functionality (after clicking on an autocomplete row). Another Container View embeds UINavigationController (to the left) which, in turn, has TrainingSearchTableViewController as its child.
The code that sets up the UISearchController and its searchBar is located in the TrainingSearchTableViewControllers' viewDidLoad and is the following:
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.sizeToFit()
searchController.delegate = self
navigationItem.titleView = searchController.searchBar
tableView.hidden = true
...
}
Nothing else was needed to fix the bug in XCode 8.2.1 and Swift 2.3, just change architecture, so that usage of UITableViewController is possible in the app and use it instead of UIViewController.
I'm working in a fresh project with Xcode 7.2, built against iOS 9.2, and testing on an iPhone 6. This project is less than a week old with a minimal framework primarily defined in a storyboard.
The storyboard has a float of UINavigationController > UITabBarController > UITableViewController, which is the one with the search code, defined below.
In my Info.plist, I have Status bar style set to UIStatusBarStyleLightContent, Status bar is initially hidden set to YES, and View controller-based status bar appearance set to No.
In the storyboard, I do not have a UISearchBar or anything else on the UITableViewController other than the prototype UITableViewCell.
Nowhere in my simple app do I override preferredStatusBarStyle and on the storyboard I have made sure all the Status Bar values are set to Light Content for every controller, just to be safe.
The UITableViewController has the following code in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
searchResultsController = MovieSearchResultsController()
searchResultsController.tableView.delegate = self
searchController = UISearchController(searchResultsController: searchResultsController)
searchController.delegate = self
searchController.dimsBackgroundDuringPresentation = true
searchController.hidesNavigationBarDuringPresentation = true
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
searchController.searchBar.tintColor = UIColor.whiteColor()
searchController.searchBar.sizeToFit()
self.tableView.delegate = self
self.tableView.tableHeaderView = searchController.searchBar
self.definesPresentationContext = true
}
When the UISearchBar is selected, the entire view shifts upwards, covering the UINavigationBar. This is great, except the status bar changes to black text and the UISearchBar disappears.
I'm pulling my hair out and have been pouring over this all day. I've read so many answers on Stack and nothing seems to work.
When I run Apple's UISearchController demo, it works exactly as intended. I've tried to replicate this exactly and I'm having no luck. I've also tried picking apart the demo and inserting my own code and it seems to work fine, short of completely redoing the project in this container, which seems highly unnecessary.
For the time being, I've just set searchController.hidesNavigationBarDuringPresentation to false and removed self.definesPresentationContext.
My Goal:
The status bar will remain with a white text color and the background color of my UINavigationBar.
The UINavigationBar will hide as the UISearchBar animates, shifting the view up.
The animation will flow smoothly, just like in the Apple demo. No jumpiness or strange delay.
Using a universal storyboard with an adaptive UISplitViewController user interface.
I want to present a search controller on the primary (master) side, using the following code (from the master view controller):
static NSString * const kCGISearchViewControllerID = #"SearchViewControllerID";
- (IBAction)searchButtonClicked:(UIBarButtonItem *)__unused sender {
SearchViewController *searchResultsController = [self.storyboard instantiateViewControllerWithIdentifier:kCGISearchViewControllerID];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
self.searchController.searchResultsUpdater = searchResultsController;
self.searchController.hidesNavigationBarDuringPresentation = NO;
[self presentViewController:self.searchController animated:YES completion:nil];
}
It initially appears to work correctly (regardless of the starting orientation):
Problems show up after autorotation (keyboard still visible, underlying content still dimmed, but the search bar has disappeared):
Upon rotating back to landscape, the search bar reappears, but its width is now wrong:
(I've also tried placing the search controller's searchBar in the titleView. While the searchBar adapts correctly, the search results controller still doesn't look right.)
What am I missing to get a presented UISearchController to animate itself properly as the UI adapts to changing size classes?
Update:
Adding self.definesPresentationContext = YES; gets the search bar/results to appear within the primary view, but the search bar is animating under that navigation bar, and isn't visible. The other issue is that the search bar height doesn't shrink, when it rotates from portrait (which has a status bar), back to landscape.
What Xcode version are you using? What iOS version on the simulator?
Tried that using Xcode 6, iOS 8.4 - That's all the code I used in the MasterVC:
class MasterViewController: UITableViewController {
#IBAction func search(sender: UIBarButtonItem) {
let searchController = UISearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
presentViewController(searchController, animated: true, completion: nil)
}
...
}
It's presented within the Master and locks the orientation of the screen! The behavior might have changed since you posted your answer.