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
Related
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.
On iOS 12 and Xcode 10.3 I had no problems with the large titles scrolling, but with the same code, Xcode 11 and iOS 13, I encounter the problem as shown in the following GIF:
While on iOS 12 I had the navigation bar with the desired behaviour, which is this:
Did someone encounter the same problem? For the rest I'm of course using the prefersLargeTitles = true and I'm sure the code I'm using it's the same for this 2 different behaviours. Thanks for any help
I found a solution for it after some investigation, so I'll share my findings since I think can help some people.
The solution is that, from iOS 13, we have to work with the UINavigationBarAppearance. Once we have created this object, we can assign it to some new properties called:
standardAppearance
compactAppearance
scrollEdgeAppearance (this one in particular was probably the cause probably of my bug)
I post an example of it as an extension:
extension UINavigationBar {
func setupLarge() {
// ... Set up here your tintColor, isTranslucent and other properties if you need
if #available(iOS 11.0, *) {
prefersLargeTitles = true
//largeTitleTextAttributes = ...Set your attributes
}
if #available(iOS 13.0, *) {
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = barTintColor
appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
standardAppearance = appearance
compactAppearance = appearance
scrollEdgeAppearance = appearance
}
}
}
I'm trying to implement a toggle for dark mode in my app - this would involve toggling the nav bar color to black when the UIViewController is already visible on the screen. I'm aware of how to do this by setting
UINavigationBar.appearance().barTintColor = .black
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
in the AppDelegate, however, this won't work in this case as it needs to be done dynamically.
I've also tried navigationController?.navigationBar.barTintColor = UIColor.white but this doesn't work either.
UPDATE:
I think a lot of the responses were confused with the purpose of this question - this is NOT at all related to the iOS13 Dark Mode which was just released - it is an INDEPENDENT dark mode feature I want to add to my app (similar to other apps like Messenger etc which had dark mode available IN-APP before iOS 13 was released). What I need to do is dynamically update the color of the UINavigationBar AFTER it has been already displayed on the screen, the same way I can change the background color of a view by doing view.backgroundColor = .white and this will update the color in real-time on screen.
Achieved this by making the nav bar translucent (in AppDelegate):
let barAppearance = UINavigationBar.appearance()
barAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: appRed]
barAppearance.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
barAppearance.shadowImage = UIImage()
barAppearance.isTranslucent = true
Next I create a view and place it behind the nav bar as follows (using SnapKit):
let coverView = UIView()
cover.snp.makeConstraints {
make in
make.left.right.top.equalTo(self)
make.bottom.equalTo(self.snp.top).offset(universalNumber(num: parent!.topbarHeight))
}
where the parent is my UIViewController and topBarHeight is:
extension UIViewController {
/**
* Height of status bar + navigation bar (if navigation bar exist)
*/
var topbarHeight: CGFloat {
return UIApplication.shared.statusBarFrame.size.height +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
}
}
Finally, to update the color I set
coverView.backgroundColor = universalWhite()
where
func universalWhite() -> UIColor {
let defaults = UserDefaults.standard
let darkMode = defaults.bool(forKey: "darkMode")
if darkMode {
return .black
} else {
return .white
}
}
You may want to check out the traits provided with the UIViewController to determine the current Interface style, instead of a manual check for versions running iOS 13+. Using this method, you can define colors in your Assets folder for both appearances.
For below iOS 13, you can use something similar to what #byaruah stated, but that is not a global effect. You should also consider using the UINavigationBar.appearance() functionality for a global approach.
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'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