I'm trying to add a UISearchController to a UIViewController that contains a UITableView (and an MKMapView too, but hopefully that's not the problem). I followed Ray Wenderlich's tutorial but I can't get the same result in terms of behaviour.
Here is my viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Setup the Search Controller
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = NSLocalizedString("Search references by project, customer or city", comment: "")
if #available(iOS 11.0, *) {
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = true
} else {
tableView.tableHeaderView = searchController.searchBar
}
definesPresentationContext = true
self.modeSelector.layer.cornerRadius = 5.0
if let split = splitViewController {
let controllers = split.viewControllers
detailViewController = (controllers[controllers.count - 1] as! UINavigationController).topViewController as? ReferenceViewController
}
self.navigationItem.rightBarButtonItem?.isEnabled = false
}
Note that the #available test in the middle is because I need to support iOS up to 9.1.
Now I see several problems:
The search bar appears right away and I can't hide it by scrolling
When I focus the search bar, the top of the tableview doesn't stick to the bottom of the navigation item:
The only major difference I see with Ray Wenderlich's sample project is that since I created my project with Xcode 9, my storyboard doesn't use top and bottom layout guides, but safe areas. Don't know if it's relevant, but that's the only thing I see.
Any idea what's going on and how I could fix this?
If you need to support iOS up to 9.1, you probably use emulator with version older than 9.1. Therefore, "maybe" obscuresBackgroundDuringPresentation doesn't affect the searchController properly, since it's only available on iOS 9.1 or newer. Add dimsBackgroundDuringPresentation to support up to 9.1:
if #available(iOS 9.1, *) {
searchController?.obscuresBackgroundDuringPresentation = false
} else {
searchController?.dimsBackgroundDuringPresentation = false
}
If this doesn't help to display as expected, I'm almost sure that the problem is about your layout constraints. Please add your current constraints if you couldn't bring your layout in compliance with safe area.
use this below line of code into your viewDidload
self.navigationController?.navigationBar.isTranslucent = false
Hope this will help you
If you are using xcode 9 (ios 11). Then the thing you really want to do is -
Use the new Broader navigation bars which are the new highlights in the ios 11 devices. But as there are many who have not shifted to ios-11, previous version devices are also taken into consideration.
For adding the search bar to the newer navigation bars I have used the following function which gives a search bar on scrolling and hides it when user scrolls the page.
func addSearchBar() {
if #available(iOS 11.0, *) {
let sc = UISearchController(searchResultsController: nil)
sc.delegate = self
let scb = sc.searchBar
scb.tintColor = UIColor.white
scb.barTintColor = UIColor.white
//Change the colors as you like them
if let textfield = scb.value(forKey: "searchField") as? UITextField {
textfield.textColor = UIColor.blue
if let backgroundview = textfield.subviews.first {
// Background color
backgroundview.backgroundColor = UIColor.white
// Rounded corner
backgroundview.layer.cornerRadius = 10;
backgroundview.clipsToBounds = true;
}
}
if let navigationbar = self.navigationController?.navigationBar {
navigationbar.barTintColor = UIColor.white
}
navigationItem.searchController = sc
navigationItem.hidesSearchBarWhenScrolling = true
}else{
//add the logic for previous version devices here.
}
I have also set the
self.navigationController?.navigationBar.prefersLargeTitles = true; in viewDidLoad as there is a bug in xcode9 and setting it from the interface builder does not work(yet).
The following method has been taken from here
Related
have a weird situation here. When I use the UISearchController, I get this appearance first (as expected)
But when you select inside the TextField to start searching, the Status Bar becomes completely white (or black if you are in dark mode)
This never used to happen. Is there some setting in UISearchController that tells it to use a certain Status Bar style when using the Search Bar?
I'd prefer it stay the color it was before selecting the TextField
---EDIT---
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItem.Style.plain, target: nil, action: nil)
frc = getFRC()
frc.delegate = self
self.resultsSearchController.delegate = self
let searchBar = self.resultsSearchController.searchBar
self.resultsSearchController.searchResultsUpdater = self
self.resultsSearchController.obscuresBackgroundDuringPresentation = false
self.resultsSearchController.extendedLayoutIncludesOpaqueBars = true
searchBar.sizeToFit()
self.tableView.tableHeaderView = searchBar
searchBar.placeholder = "Catalog Search"
searchBar.barTintColor = UIColor.darkAqua
searchBar.searchTextField.backgroundColor = UIColor.white
self.definesPresentationContext = true
searchBarHeight = searchBar.frame.height
do {
try frc.performFetch()
} catch {
error.tryError(tryMessage: "Perform initial fetch", loc: self)
}
if tutorials.catalog {
createTutorialTab(segueNameOnOpen: "catalogTutorial")
}
}
You use this function to change status bar colors, it's a kind of hack to deal with status bar ;). If you are using one theme status bar in whole app , call this function from didFinishLaunching in AppDelegate.
func changeStatusBar(backgroundColor: UIColor, contentColor:UIColor) {
if let statusBar = UIApplication.shared.value(forKey: "statusBar") as? UIView {
statusBar.backgroundColor = backgroundColor
statusBar.setValue(contentColor, forKey: "foregroundColor")
}
}
after the new update, I've noticed that my UISearchController isn't acting like it did before.
First, the UITextField no longer has a white background. I was trying to figure out why this is happening, but have had no luck. This is how I'm creating it.
var resultsSearchController = UISearchController(searchResultsController: nil)
self.resultsSearchController.delegate = self
let searchBar = self.resultsSearchController.searchBar
self.resultsSearchController.searchResultsUpdater = self
self.resultsSearchController.obscuresBackgroundDuringPresentation = false
self.resultsSearchController.extendedLayoutIncludesOpaqueBars = true
searchBar.sizeToFit()
self.tableView.tableHeaderView = searchBar
searchBar.placeholder = "Catalog Search"
searchBar.barTintColor = UIColor.darkAqua
As best I can tell, the default UITextField default appearance seems to have changed is my guess.
Just wondering how to change it back, if possible.
---EDIT---
I attempted to do as suggested and added this code to viewDidLoad()
if #available(iOS 13.0, *) {
overrideUserInterfaceStyle = UIUserInterfaceStyle.light
} else {
// Fallback on earlier versions
}
as well as adding this to the UISearchController code
if #available(iOS 13.0, *) {
self.resultsSearchController.overrideUserInterfaceStyle = UIUserInterfaceStyle.light
} else {
// Fallback on earlier versions
}
No combination of both of those codes was able to change it so that that UITextField presented as expected.
Well, turns out I'm dumb. Its much easier that I was trying to make it for myself.
All I ended up doing was accessing the UITextField in the searchBar for the UISearchController
Adding this:
searchBar.searchTextField.backgroundColor = UIColor.white
With the code I already had seems to make it look normal again.
I have my UISearchBar set up as follows:
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false // Allow user to tap on results
searchController.searchBar.placeholder = "Search patients" // Placeholder
searchController.searchBar.barStyle = .blackOpaque
searchController.searchBar.tintColor = colors.text // Cancel button tint
navigationItem.searchController = searchController // Set the searchController
navigationItem.hidesSearchBarWhenScrolling = true // Auto-hide search when user scrolls
This is how it looks on iOS 12:
vs iOS 13:
What's changed in iOS 13? I've tried going through the different barStyles, and also setting .isTranslucent to false - no effect for either. Light/dark mode also don't change anything.
The other change is hiding the search bar - on iOS 12 if I scrolled upwards a little the search bar would hide (didn't matter if the table was populated or not). With iOS 13, once the search bar has appeared (ie the user has swiped down), it cannot be hidden again. Anyone know of a fix for this too?
I get something similar problem with you. I don't know why this happen in currently iOS 13 and work properly in older version. But i have found the solution by adding this function to your searchBar.
if #available(iOS 13.0, *) {
searchBar.searchTextField.backgroundColor = UIColor.white
}
Preview after fixing:
How about to use searchBarStyle as default and change the searchTextField background color?
if #available(iOS 13.0, *) {
searchBar.searchBarStyle = .default
searchBar.searchTextField.backgroundColor = UIColor.black.withAlphaComponent(0.1)
}
For setting in globally like in AppDelegate:
if #available(iOS 13, *) {
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).backgroundColor = .anyColor
}
searchController.searchBar.searchTextField.backgroundColor = UIColor.black does the job as a workaround. The selector is new in iOS 13.
I've filed a report on feedback assistant anyway as I do believe this to be unexpected behaviour.
iOS 13+.
searchController.searchBar.searchTextField.backgroundColor = UIColor.white
I'm using a UISearchController for search in a UITableViewController that also supports pull-to-refresh via UIRefreshControl.
The setup is very simple in a reduced Demo project
override func viewDidLoad() {
extendedLayoutIncludesOpaqueBars = true
title = searchTerm ?? "Search"
super.viewDidLoad()
setupSearch()
setupRefresh()
}
private func setupSearch() {
searchController.searchResultsUpdater = self
navigationItem.searchController = searchController
definesPresentationContext = true
//
// If this is set to `true` (which is also the default),
// UISearchBar and UIRefreshcontroll are buggy
//
navigationItem.hidesSearchBarWhenScrolling = true
}
private func setupRefresh() {
refreshControl = UIRefreshControl()
refreshControl?.addTarget(self, action: #selector(refresh), for: .valueChanged)
}
This worked in iOS 12, but now in iOS 13 (compiled for iOS 13 with Xcode 11 GM), the Refresh Animation is broken
The only "fix" I have found so far is to set navigationItem.hidesSearchBarWhenScrolling to false, but this obviously causes the Searchbar to always stay on screen, even when scrolling.
Here is a sample project demonstrating the issue: https://github.com/iv-mexx/UISearchControl-UIRefreshControl-iOS13-Bug/tree/feature/ios13
Update: This still remains broken in Xcode 11 GM Seed 2
If turning on large titles is an option for your app, this seems to workaround the issue as well.
This might be why Apple isn't running into the issue in their own apps.
I have a requirement in which I have to use a UINavigationBar with a red large title.
Currently, I have the following code:
func prepareNavigationController() {
let navController = UINavigationController(rootViewController: self)
navController.navigationBar.prefersLargeTitles = true
navigationItem.searchController = UISearchController(searchResultsController: nil)
navigationItem.hidesSearchBarWhenScrolling = false
navController.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor.rawValue: UIColor.red]
}
But it's not actually tinting the title label to red. This is the result:
But changing prefersLargeTitles to false does the right thing, and my title is red.
navController.navigationBar.prefersLargeTitles = false
I am not entirely sure if this is a bug since at the time of this writing we are still in the first beta, or if this is intentional behavior, mostly because I haven't any of Apple's apps color the large titles before. Is there any way to actually get the large title to have any color I want?
There is a new UINavigationBar property "largeTitleTextAttribute" that should help with this.
largeTitleTextAttribute
Here is a sample code you can add to your view controllers viewDidLoad method
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.blue]
Here is a sample code and screenshot without the largeTitleTextAttributes set, but the barStyle is set to .black
navigationController?.navigationBar.barStyle = .black
Here is a screenshot without the largeTitleTextAttributes set, but the barStyle is set to .default
navigationController?.navigationBar.barStyle = .default
The way you do this in iOS 13 has changed, you now use UINavigationBarAppearance class like this…
let appearance = UINavigationBarAppearance(idiom: .phone)
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.systemRed]
appearance.titleTextAttributes = [.foregroundColor: UIColor.systemRed]
appearance.backgroundColor = .white
navigationItem.standardAppearance = appearance
navigationItem.scrollEdgeAppearance = appearance
Not sure if it's a bug in beta 1 & 2, but here is a way to set the color. It's a bit of a "hacky" workaround, but it should work until Apple fixes this. In both the Objective-C and Swift version, this code goes in the viewDidAppear: method.
Objective-C:
dispatch_async(dispatch_get_main_queue(), ^{
for (UIView *view in self.navigationController.navigationBar.subviews) {
NSArray <__kindof UIView *> *subviews = view.subviews;
if (subviews.count > 0) {
UILabel *label = subviews[0];
if (label.class == [UILabel class]) {
[label setTextColor:[UIColor redColor]];
}
}
}
});
Swift:
DispatchQueue.main.async {
for view in self.navigationController?.navigationBar.subviews ?? [] {
let subviews = view.subviews
if subviews.count > 0, let label = subviews[0] as? UILabel {
label.textColor = UIColor.red
} } }
If using storyboard, just change "Large Title Text Attributes" Title Color at Navigation Bar Attribute Inspector:
Here's the working code to use large titles and sets the text color of small and large titles to white, both on iOS11+ and on older iOS versions.
// Will apply to versions before iOS 11
navigationController?.navigationBar.titleTextAttributes = [
NSAttributedStringKey.foregroundColor: UIColor.white
]
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.largeTitleTextAttributes = [
NSAttributedStringKey.foregroundColor: UIColor.white
]
}
(There used to be a bug in Xcode, but it now appears to be fixed)