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.
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.
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 have a UISearchBar integrated in my navigation bar with this code:
// not needed because it's default
navigationItem.hidesSearchBarWhenScrolling = true
self.navigationItem.searchController = searchController
Also I add a custom bookmark icon like this:
searchController.searchBar.setImage(icon, for: .bookmark, state: .normal)
searchController.searchBar.showsBookmarkButton = true
searchController.searchBar.layoutIfNeeded()
This produces this weird look in iOS 11.4 and 12.1.4
It seems that the text field doesn't clip the icon and also doesn't apply the fade animation like for the placeholder and the search icon.
Do you guys see some error on my side?
If not, can someone reproduce this?
Then it is a bug and I will file a radar.
Okay I worked around the issue by doing this:
searchController.searchBar.allSubviews.forEach { $0.clipsToBounds = true }
Using this extension to get all nested subviews:
extension UIView {
var allSubviews: [UIView] {
return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews }
}
}
But this is a bit hacky so other solutions are appreciated :)
Try to do use clipToBounds instead of layoutIfNeeded()
searchController.searchBar.setImage(icon, for: .bookmark, state: .normal)
searchController.searchBar.showsBookmarkButton = true
searchController.searchBar.clipsToBounds = true
I have an UIRefreshControl which works on every device except iPhone 8 (works neither on a physical device or an emulator).
On an iPhone 8 the refresh indicator stops at 3/4 to the end and never calls refresh
This is how it looks like when you pull it all the way down:
Creating the refresh control:
lazy var refreshControl: UIRefreshControl = {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action:
#selector(TeamViewController.handleRefresh(_:)),
for: UIControlEvents.valueChanged)
refreshControl.tintColor = UIColor.red
return refreshControl
}()
Adding to the view:
if #available(iOS 10.0, *) {
myCollectionView.refreshControl = refreshControl
} else {
myCollectionView.addSubview(refreshControl)
}
I had the same problem and was caused by me setting the view controller to freeform in the storyboard as the simulated size. Changing it back to fixed resolved the issue.
Can also be fixed by using:
override func viewDidLayoutSubviews() {
refreshControl?.didMoveToSuperview()
super.viewDidLayoutSubviews()
}
UIRefreshControl needs to be pulled down too far
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