iOS 11 navigationBar with both segmentedControl and searchBar - uinavigationbar

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.

Related

Visible UISearchBar changes UINavigationBar background color

A tableview controller is embedded into a navigation controller.
I programmatically added a search bar to the tableview controller's navigation bar. I only changed the navigation bar Background color into something different than Default (purple) - all the rest I left default.
class TableViewController: UITableViewController {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesSearchBarWhenScrolling = true
navigationItem.searchController = searchController
}
}
Code above is reduced to bare minimum for demonstration purpose.
All done with Xcode 11 (11A420a).
I ran the project in iOS 12.0 and 13.0 simulators and devices.
iOS 13.0
The search bar shows upon start.
Navigation bar background color is correctly presented.
While scrolling, navigation bar background color remains correct.
With iOS 13.0, all works as expected!
iOS 12.0
The search bar doesn't show upon start.
Navigation bar background color is correctly presented.
While scrolling, navigation bar background color goes white as soon search bar is visible.
I tried to change all kind of color setting in storyboard as well as properties programmatically. I didn't succeed in changing the navigation bar background color when search bar is visible.
It seems (?!) that the navigation bar foreground looses transparency when search bar becomes visible.
If I use a Bar Tint color of the navigation bar (!= Default), all works as expected (and as with iOS 13.0), but I loose the gradient effect, which I would like to keep.
What did I miss?
How can I avoid this?
Bug?
I had some luck with the navigationItem.scrollEdgeAppearance property when facing a similar problem. For example:
vc.navigationItem.scrollEdgeAppearance?.backgroundColor = .red
This is only available on iOS 13 though.
Here's what I ended up doing to get correct colors in the navigation bar while allowing the search controller's scroll bar to hide during scroll:
if #available(iOS 13.0, *) {
let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial)
navbar.standardAppearance.backgroundEffect = blurEffect
navbar.standardAppearance.backgroundColor = appMainColor.withAlphaComponent(0.75)
navbar.standardAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
navbar.compactAppearance = nil
navbar.scrollEdgeAppearance = navbar.standardAppearance.copy()
navitem.standardAppearance = nil
navitem.compactAppearance = nil
navitem.scrollEdgeAppearance = nil
}
I don't know if it is the look you're going for but I found if you disable hideSearchBarWhenScrolling the background stops changing color. However, the search bar is always there.
Add this to viewDidLoad():
navigationItem.hidesSearchBarWhenScrolling = false

iOS 11 - Weird UINavigationBar and UICollectionView behavior

I have a UINavigationController the root view controller of which is a UIViewController with a UICollectionView. The collection has constraint to the left, right, bottom and top. I've checked the prefersLargeTitles attribute in the UINavigationController, so I have big titles. I expected that, during the scroll, the UINavigationBar would automatically collapse, but I had to disable the large titles in my root view controller and add this line of code in my viewWillAppear :
navigationItem.largeTitleDisplayMode = .automatic
Then, I added also the search bar to the UINavigationBar, in this way :
let searchController = UISearchController.init(searchResultsController: nil)
searchController.searchResultsUpdater = self
navigationItem.searchController = searchController
Everything is perfect, visually.
Now, when I scroll down, the UICollectionView suddenly stick to the UINavigationBar which collapses. When I tap on the status bar to go to the top of the collection, a white space appears between the large title and the status bar. Is there a specific procedure that, for some reason, I'm not following? Do you have any solution?
EDIT:
Here you can find the video of the problem
When you see the collection that sticks to the UINavigationBar I confirm you that it's happening in a unnatural way. The last "bug" happens when I tap on the status bar.

UISearchBarController iOS 11 issue - SearchBar and scope buttons overlap

Referred here and here. No answer in first link. In the second link, though the answer is not accepted, but the link to apple developer forum gives error.
Before iOS 11 :
iOS 11 :
Note : Same device same code.
Also, this would mean, all apps using this feature have to be republished ?
Adding these lines fixed it for me:
override func viewDidLayoutSubviews() {
self.searchController.searchBar.sizeToFit()
}
I can get the initial appearance to display correctly in iOS11 using the following code (as per greg's answer):
[self.searchController.searchBar sizeToFit];
if (#available(iOS 11.0, *)) {
self.navigationItem.searchController = self.searchController;
self.navigationItem.hidesSearchBarWhenScrolling = NO;
} else {
// Fallback on earlier versions
self.tableView.tableHeaderView = self.searchController.searchBar;
}
However, if the app is backgrounded then restored while the search bar was active, the appearance would end up overlapped as shown in Nitish's second screenshot above.
I was able to fix that with the following workaround:
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
self.searchController.searchBar.showsScopeBar = NO;
[self.searchController.searchBar sizeToFit];
self.searchController.searchBar.showsScopeBar = YES;
[self.searchController.searchBar sizeToFit];
}];
(I'm still working on how to workaround the layout issues following an interface orientation change while the search bar is active - that still ends up overlapped.)
In the radar that Ray Wenderlich filed, #benck posted this answer from WWDC, which, if I'm not mistaken, hasn't been posted yet.
Per your comments, your UISearchController's UISearchBar has been assigned to your UITableView's tableHeaderView. In iOS 11, you should instead be assigning your UISearchController to the searchController property of your view's navigationItem. You no longer need to assign the UISearchBar anywhere. See Apple's documentation on this new property.
I met the same issue on my app, my solution is in iOS 11, using apple suggested new way for searchBar which is in navigationItem, otherwise, using the old way. My code in viewDidLoad() as below:
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = false
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
searchViewHeight.constant = 0
} else {
searchView.addSubview(searchController.searchBar)
}
I have two IBOutlets: searchView and searchViewHeight:
#IBOutlet var searchView: UIView!
#IBOutlet var searchViewHeight: NSLayoutConstraint! // new added for iOS 11
Before iOS 11, my viewController's hierarchy as below:
I have a searchView which height is 44 to contains my searchController's searchBar view. It's under navigation bar.
In iOS 11, I add a new IBOutlet for searchView's height constraint, and set its constant to 0, hide this container view. And add searchController as a part of navigation item.
See apple's document:
https://developer.apple.com/documentation/uikit/uinavigationitem/2897305-searchcontroller
One more thing is under iOS 11, the searchBar's textField background color is little darker than navigation bar color by default. For consistency, you can change it to white, the below code will work both for iOS11 and its prior:
if let textField = searchController.searchBar.value(forKey: "searchField") as? UITextField {
if let backgroundView = textField.subviews.first {
// Search bar textField background color
backgroundView.backgroundColor = UIColor.white
// Search bar textField rounded corner
backgroundView.layer.cornerRadius = 10
backgroundView.clipsToBounds = true
}
}
I think that the solution is to add the Search Bar in the Navigation Bar:
navigationController?.navigationBar.prefersLargeTitles = true // Navigation bar large titles
navigationItem.title = "Contacts"
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
navigationController?.navigationBar.barTintColor = UIColor(displayP3Red: 0/255, green: 150/255, blue: 136/255, alpha: 1.0)
let searchController = UISearchController(searchResultsController: nil) // Search Controller
navigationItem.hidesSearchBarWhenScrolling = false
navigationItem.searchController = searchController
You can find an example for UISearchBarController - SearchBar and scope buttons overlap here.
I had the same issue in iOS 11.
Contrary to some of the comments here, if I look at your screenshots you DONT want to set it as the navigationItem because you don't have a UINavigationController setup.
Neither do you want to add the searchBar in the header of the tableView because for some reason it can't cope with the scopeBar
So what I did to fix it:
To get a UISearchBar with scopes over your tableView, use a UIViewController in interface builder not a UITableViewController.
Place a UISearchBar and a UITableView inside the view controller and wire them up properly (delegates, dataSource, etc).
Don't forget to change your swift file to UIViewController instead of UITableViewController as well and change it accordingly. (add a tableView property and connect it via IBOutlet, change the delegates for the tableView etc)
Then in interface builder, use autoLayout guides so the searchBar sits on top of the tableView
In interface builder when you activate the scope bar it will look totally weird but don't panic, it will be fine. I guess Apple screwed the rendering n interface builder when they changed the behavior to work with UINavigationController... anyway...
Then everything works as it should and look like this (in my case I present it the vc in a popover but that doesn't matter)

UISearchController weird behavior on dismissing it

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.

UISearchController and UISearchBar are not animating properly and are changing the Status Bar

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.

Resources